package com.fowo.api.documentCirculation.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import org.springframework.jdbc.core.JdbcTemplate;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fowo.api.common.model.PageAndStatistics;
import com.fowo.api.common.model.PageSearch;
import com.fowo.api.common.model.PageSearchFilter;
import com.fowo.api.common.model.RException;
import com.fowo.api.common.util.ListUtils;
import com.fowo.api.common.util.SheetUtils;
import com.fowo.api.documentCirculation.entity.DataTaskLogs;
import com.fowo.api.documentCirculation.mapper.DataTaskLogsMapper;
import com.fowo.api.documentCirculation.model.*;
import com.fowo.api.documentCirculation.model.constant.DataTaskMappingTypes;
import com.fowo.api.documentCirculation.service.DataTaskMappingService;
import com.fowo.api.documentCirculation.service.DataTaskService;
import com.fowo.api.documentCirculation.service.DocumentCirculationRuntimeService;
import com.fowo.api.documentCirculation.sql.SqlBuilder;
import com.fowo.api.documentCirculation.util.NumberUtil;
import com.fowo.api.sys.model.NewSerialRequest;
import com.fowo.api.sys.service.SysSerialService;
import com.fowo.api.user.model.JwtUserInfo;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.runtime.function.AbstractFunction;
import com.googlecode.aviator.runtime.type.AviatorObject;
import com.googlecode.aviator.runtime.type.AviatorString;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.RegExUtils;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 单据流转运行时服务实现
 *
 * @author yl_ls
 */
@Service
public class DocumentCirculationRuntimeServiceImpl implements DocumentCirculationRuntimeService, InitializingBean {
    private static final String VAR_CURRENT_USER_ID = "currentUserId";
    /**
     * 特定用于识别是否在过滤中引用默认表别名的正则表达式
     */
    public static final String FILTER_T_REGEX = ".*\\bt\\..*";
    /**
     * 特定用于识别是否在过滤中引用了目标主表别名的正则表达式
     */
    public static final String FILTER_TM_REGEX = ".*\\btm\\..*";
    @Resource
    private DataTaskService dataTaskService;
    @Resource
    private DataTaskMappingService dataTaskMappingService;
    @Resource
    private DataTaskLogsMapper dataTaskLogsMapper;
    @Resource
    private DataSource dataSource;
    @Resource
    protected SysSerialService sysSerialService;
    @Resource
    protected DataTaskServiceInvoker serviceInvoker;
    private final IdentifierGenerator identifierGenerator = new DefaultIdentifierGenerator();
    @Resource
    private JdbcTemplate jdbcTemplate;

    private SqlBuilder.DbType getDbTypeFromDataSource() {
        if (dataSource == null) {
            throw new RuntimeException("数据源参数不能为空!");
        }
        VendorDatabaseIdProvider vendorDatabaseIdProvider = new VendorDatabaseIdProvider();
        Properties properties = new Properties();
        properties.put("MySQL", "MySql");
        properties.put("SQL Server", "SqlServer");
        vendorDatabaseIdProvider.setProperties(properties);
        final String databaseId = vendorDatabaseIdProvider.getDatabaseId(dataSource);
        try {
            return SqlBuilder.DbType.valueOf(databaseId);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("单据流转运行时服务当前不支持此类型数据库！");
        }
    }

    private Optional<SqlBuilder.DbType> optionalDbType = Optional.empty();

    public SqlBuilder.DbType currentDbType() {
        if (optionalDbType.isEmpty()) {
            optionalDbType = Optional.of(getDbTypeFromDataSource());
        }
        return optionalDbType.orElseThrow();
    }

    private Long currentUserId() {
        final JwtUserInfo jwtUserInfo = JwtUserInfo.fromHeader();
        if (jwtUserInfo != null && jwtUserInfo.getUserId() > 0) {
            return jwtUserInfo.getUserId();
        }
        return null;
    }

    @Override
    public DataTaskRuntimeModel getModelById(Long id) throws Exception {
        final DataTaskVo vo = dataTaskService.getVoById(id);
        return getRuntimeModelByVo(vo);
    }

    @Override
    public DataTaskRuntimeModel getModalByCode(String code) throws Exception {
        final DataTaskVo vo = dataTaskService.getVoByCode(code);
        return getRuntimeModelByVo(vo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void pushDown(PushDownRequestPo requestPo) throws Exception {
        final DataTaskRuntimeModel model = getModelById(requestPo.getDataTaskId());
        for (PushDownRequestPo.PushDownEntity entity : requestPo.getEntities()) {
            pushDownEntity(model, entity);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void pullUp(PushDownRequestPo requestPo) throws Exception {
        final Long opId = (Long) identifierGenerator.nextId(null);
        final Long userId = currentUserId();
        final DataTaskRuntimeModel model = getModelById(requestPo.getDataTaskId());
        if (model.getSubBranch() == null) {
            throw new RException("暂未实现单表勾稽处理");
        }
        final Map<String, Object> overrideVars = new HashMap<>();
        overrideVars.put(VAR_CURRENT_USER_ID, userId);
        // 检查目标数据是否存在
        final Map<String, Object> toMainMap = SqlBuilder.select(currentDbType(), model.getMainBranch().getToTable())
                .eq("del_flag", 0)
                .eq("id", requestPo.getToMainId())
                .executeSelect();
        if (toMainMap == null) {
            throw new RException("当前目标数据不存在！");
        }
        // 获取历史记录
        final List<DataTaskLogs> oldTaskLogs = getTaskLogs(requestPo.getDataTaskId(), requestPo.getToMainId());
        for (PushDownRequestPo.PushDownEntity entity : requestPo.getEntities()) {
            // 取源主键
            Object mainDataId = entity.getFromMap().get("id");
            // 取源主表记录
            final Map<String, Object> mainTable = getMainFromTable(model, model.getMainBranch(), mainDataId);
            if (mainTable == null) {
                throw new RException("未找到主来源记录！");
            }
            if (!isCanPushDown(model.getMainBranch(), mainTable)) {
                throw new RException("当前主来源记录状态不可运行下推！");
            }
            SqlBuilder sqlBuilder = SqlBuilder.select(currentDbType(), model.getSubBranch().getFromTable());
            Map<String, String> selectJoinMaps = appendMappingFieldsJoin(sqlBuilder, model.getSubBranch().getMappings());
            if (!selectJoinMaps.isEmpty()) {
                sqlBuilder.selectExpression("t.*", null);
                for (Map.Entry<String, String> selectJoinEntry : selectJoinMaps.entrySet()) {
                    sqlBuilder.selectExpression(selectJoinEntry.getValue(), selectJoinEntry.getKey());
                }
            }
            List<Map<String, Object>> records = sqlBuilder
                    .in("id", entity.getChildren().stream().map(e -> e.getFromMap().get("id")))
                    .executeSelectList();
            if (records.isEmpty() || records.size() < entity.getChildren().size()) {
                throw new RException("所选源数据在操作期间发生变更！");
            }
            for (PushDownRequestPo.PushDownEntity child : entity.getChildren()) {
                // 取源子表记录
                Map<String, Object> record = ListUtils.find(records,
                        r -> NumberUtil.compareTo(r.get("id"), child.getFromMap().get("id")) == 0);
                if (record == null) {
                    throw new RException("所选源数据在操作期间发生变更！源子表记录未找到！");
                }
                if (!isCanPushDown(model.getSubBranch(), record, true)) {
                    throw new RException("当前子来源记录(ID:" + record.get("id") + ")状态不可运行下推！");
                }
                DataTaskLogs oldTaskLog = ListUtils.find(oldTaskLogs,
                        l -> NumberUtil.compareTo(l.getFromSubId(), child.getFromMap().get("id")) == 0);
                // 取要操作的数量
                Number toTableCrossCheck = (Number) child.getToMap()
                        .get(model.getSubBranch().getFirstToTableCrossCheckFields());
                if (oldTaskLog != null && NumberUtil.compareTo(oldTaskLog.getNumChange(), toTableCrossCheck) == 0) {
                    // 此数据没有变化，忽略
                    continue;
                }
                if (oldTaskLog != null) {
                    // 有历史记录，运行回滚处理
                    pullUpdate(requestPo, opId, userId, model, oldTaskLog, record, entity, child, toTableCrossCheck,
                            overrideVars);
                } else {
                    pullInsert(requestPo, opId, userId, model, overrideVars, mainDataId, child, record,
                            toTableCrossCheck);
                }
            }

            JSONObject jsonEntity = (JSONObject) JSONObject.toJSON(entity);
            jsonEntity.put("toMap", toMainMap);
            afterPullUp(model, jsonEntity);
        }
    }

    /**
     * Pull 作废，和 Cancel 的意义不同，不会删除目标子表数据
     * @param requestPo 作废请求
     * @throws Exception 错误
     */
    @Transactional(rollbackFor = Exception.class)
    public void pullVoid(PullCancelRequestPo requestPo) throws Exception {
        // 查任务表
        final DataTaskRuntimeModel model = getModelById(requestPo.getDataTaskId());
        if (model == null) {
            throw new Exception("指定的配置不存在！");
        }
        // 查历史记录表数据
        List<DataTaskLogs> dataTaskLogsList = dataTaskLogsMapper.selectList(
                Wrappers.<DataTaskLogs>query()
                        .eq("data_task_id", requestPo.getDataTaskId())
                        .eq("to_main_id", requestPo.getToMainId())
                        .ne("cancelled", 1)
                        .orderByDesc("create_time"));
        if (CollUtil.isEmpty(dataTaskLogsList)) {
            // 没有可操作数据
            return;
        }
        Set<Long> fromSubIdSet = dataTaskLogsList.stream().map(DataTaskLogs::getFromSubId).collect(Collectors.toSet());
        List<Map<String, Object>> fromSubs = SqlBuilder.select(currentDbType(), model.getSubBranch().getFromTable())
                .in("id", new ArrayList<>(fromSubIdSet))
                .eq("del_flag", 0)
                .executeSelectList();
        if (fromSubs.size() != fromSubIdSet.size()) {
            // 存在已删除的源子表
            String delFromSubIds = fromSubIdSet.stream()
                    .filter(subId -> fromSubs.stream().noneMatch(f -> Objects.equals(f.get("id"), subId)))
                    .map(Object::toString).collect(Collectors.joining(", "));
            throw new RException("源子表记录(" + delFromSubIds + ")已删除，无法撤消！");
        }
        for (DataTaskLogs dataTaskLogs : dataTaskLogsList) {
            // 取对应的源子表
            Map<String, Object> fromSub = ListUtils.find(fromSubs, s -> Objects.equals(dataTaskLogs.getFromSubId(), s.get("id")));
            // 红冲类操作数量反向
            boolean revNumber = Boolean.TRUE.equals(model.getSubBranch().getIsHCCrossChecking());
            BigDecimal numChange = dataTaskLogs.getNumChange();
            if (revNumber) {
                numChange = numChange.negate();
            }
            // 源表当前数量
            Number crossCheckNum = NumberUtil.asNumber(fromSub.get(model.getSubBranch().getFromTableCrossCheckFields()));
            Number cumulative = null;
            if (StringUtils.hasText(model.getSubBranch().getFromTableCumulativeFields())) {
                cumulative = NumberUtil.asNumber(fromSub.get(model.getSubBranch().getFromTableCumulativeFields()));
            }
            // 归还数量
            crossCheckNum = NumberUtil.add(crossCheckNum, numChange);
            if (cumulative != null) {
                cumulative = NumberUtil.subtract(cumulative, numChange);
            }
            fromSub.put(model.getSubBranch().getFromTableCrossCheckFields(), crossCheckNum);
            if (cumulative != null) {
                fromSub.put(model.getSubBranch().getFromTableCumulativeFields(), cumulative);
            }
        }
        // 已勾数量为 0 又有回退字段的，需要清空已勾数量字段
        if (StringUtils.hasText(model.getSubBranch().getFromTableCrossBackFields())) {
            for (Map<String, Object> fromSub : fromSubs) {
                if (NumberUtil.compareTo(0, fromSub.get(model.getSubBranch().getFromTableCrossCheckFields())) == 0) {
                    fromSub.put(model.getSubBranch().getFromTableCrossCheckFields(), null);
                }
            }
        }

        // 更新源子表
        for (Map<String, Object> fromSub : fromSubs) {
            SqlBuilder updateBuilder = SqlBuilder.update(currentDbType(), model.getSubBranch().getFromTable())
                    .eq("id", fromSub.get("id"));
            updateBuilder.set(model.getSubBranch().getFromTableCrossCheckFields(), fromSub.get(model.getSubBranch().getFromTableCrossCheckFields()));
            if (StringUtils.hasText(model.getSubBranch().getFromTableCumulativeFields())) {
                updateBuilder.set(model.getSubBranch().getFromTableCumulativeFields(), fromSub.get(model.getSubBranch().getFromTableCumulativeFields()));
            }
            updateBuilder.executeUpdate();
        }

        // 更新历史记录
        SqlBuilder.update(currentDbType(), "data_task_logs")
                .in("id", dataTaskLogsList.stream().map(DataTaskLogs::getId).collect(Collectors.toList()))
                .set("cancelled", 1)
                .executeUpdate();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void pullCancel(PullCancelRequestPo requestPo) throws Exception {
        pullVoid(requestPo);
    }

    /**
     * @deprecated
     * @param requestPo
     * @throws Exception
     */
    @Deprecated(forRemoval = true, since = "2024/8/5")
    @Transactional(rollbackFor = Exception.class)
    public void pullCancelOld(PullCancelRequestPo requestPo) throws Exception {
        final Long userId = currentUserId();
        // 查任务表
        final DataTaskRuntimeModel model = getModelById(requestPo.getDataTaskId());
        if (model == null) {
            throw new Exception("指定的配置不存在！");
        }
        // 查历史记录表数据
        List<DataTaskLogs> dataTaskLogsList = dataTaskLogsMapper.selectList(
                Wrappers.<DataTaskLogs>query()
                        .eq("data_task_id", requestPo.getDataTaskId())
                        .eq("to_main_id", requestPo.getToMainId())
                        .ne("cancelled", 1)
                        .orderByDesc("create_time"));
        if (dataTaskLogsList != null) {
            for (DataTaskLogs dataTaskLogs : dataTaskLogsList) {
                if (dataTaskLogs.getCancelled()) {
                    throw new RException("该[" + dataTaskLogs.getId() + "]条数据已做过取消，不可进行二次取消");
                }
                // 检查目标数据是否存在
                final Map<String, Object> toMainMap = SqlBuilder
                        .select(currentDbType(), model.getMainBranch().getToTable())
                        .eq("del_flag", 0)
                        .eq("id", requestPo.getToMainId())
                        .executeSelect();
                if (toMainMap == null) {
                    throw new RException("当前目标数据不存在！");
                }
                List<Map<String, Object>> records = SqlBuilder
                        .select(currentDbType(), model.getSubBranch().getFromTable())
                        .eq("id", dataTaskLogs.getFromSubId())
                        .executeSelectList();
                if (records.isEmpty()) {
                    throw new RException("所选源数据不存在！");
                }
                // 取源子表记录
                Map<String, Object> record = ListUtils.find(records,
                        r -> NumberUtil.compareTo(r.get("id"), dataTaskLogs.getFromSubId()) == 0);
                if (record == null) {
                    throw new RException("所选源数据在操作期间发生变更！源子表记录未找到！");
                }
                // 1.返回来源表的数量字段
                // 取出需要返还的数量 & 日志表读取数量
                BigDecimal uiFromTableCrossCheck = dataTaskLogs.getNumChange();
                if (uiFromTableCrossCheck == null) {
                    throw new RException("操作异常，请求缺少关键信息");
                }
                Map<String, Object> backDetail = new HashMap<>();
                record.get(model.getSubBranch().getFromTableCrossCheckFields());
                if (Boolean.TRUE.equals(model.getMainBranch().getIsNegative())) {
                    backDetail.put(model.getSubBranch().getFromTableCrossCheckFields(),
                            NumberUtil.add(uiFromTableCrossCheck.abs(), new BigDecimal(
                                    record.get(model.getSubBranch().getFromTableCrossCheckFields()).toString())));
                } else {
                    backDetail.put(model.getSubBranch().getFromTableCrossCheckFields(),
                            NumberUtil.add(uiFromTableCrossCheck, new BigDecimal(
                                    record.get(model.getSubBranch().getFromTableCrossCheckFields()).toString())));
                }
                if (StringUtils.hasText(model.getSubBranch().getFromTableCumulativeFields())) {
                    backDetail.put(model.getSubBranch().getFromTableCumulativeFields(),
                            NumberUtil.subtract(
                                    new BigDecimal(
                                            record.get(model.getSubBranch().getFromTableCumulativeFields()).toString()),
                                    uiFromTableCrossCheck.abs()));
                }
                // 回源有后备数量字段，而且当前数量字段为 0 时，回写空
                if (StringUtils.hasText(model.getSubBranch().getFromTableCrossBackFields())) {
                    backDetail.put(model.getSubBranch().getFromTableCrossCheckFields(), null);
                }
                SqlBuilder updateFromBuilder = SqlBuilder.update(currentDbType(), model.getSubBranch().getFromTable());
                updateFromBuilder.setMap(backDetail);
                updateFromBuilder.eq("id", dataTaskLogs.getFromSubId());
                // 更新记录原数量，作为同步版本安全识别
                // updateFromBuilder.eq(model.getSubBranch().getFromTableCrossCheckFields(),
                // backDetail.get(model.getSubBranch().getFromTableCumulativeFields()));
                if (updateFromBuilder.executeUpdate() != 1) {
                    throw new RException("回写更新源子表异常！");
                }

                // 2.假删除目标表的数据
                // SqlBuilder updateToBuilder = SqlBuilder.update(currentDbType(),
                // model.getSubBranch().getToTable()).set("del_flag", 1).eq("id",
                // dataTaskLogs.getToSubId());
                // if (updateToBuilder.executeUpdate() != 1) {
                // throw new RException("回写更新目标子表异常！");
                // }

                // 3.更新日志表为撤销状态
                SqlBuilder updateLogsBuilder = SqlBuilder.update(currentDbType(), "data_task_logs");
                updateLogsBuilder.set("cancelled", 1);
                updateLogsBuilder.set("canceller", userId);
                updateLogsBuilder.set("cancellation_time", new Timestamp(System.currentTimeMillis()));
                updateLogsBuilder.eq("id", dataTaskLogs.getId());
                if (updateLogsBuilder.executeUpdate() != 1) {
                    throw new RException("回写更新日志表异常！");
                }
            }
        } else {
            throw new RException("当前需要取消的数据不存在！");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void pullAssociation(Long dataTaskId, Long toMainId) throws Exception {
        final Long userId = currentUserId();
        final Long opId = (Long) identifierGenerator.nextId(null);
        // 查任务表
        final DataTaskRuntimeModel model = getModelById(dataTaskId);
        if (model == null) {
            throw new Exception("指定的配置不存在！");
        }
        if (StrUtil.isEmpty(model.getSubBranch().getToTableIdColumn())) {
            throw new Exception("该数据事务未配置目标表ID列");
        }
        /**
         * 根据目标主键Id查询子表导入的商品信息
         */
        //错误提示列
        String toField = null;
        String fieldDescription = null;
        List<DataTaskMappingItemVo> dataTaskMappingItemVoList = model.getSubBranch().getMappings().stream().filter(item -> item.getErrorEcho() == true).collect(Collectors.toList());
        if (!dataTaskMappingItemVoList.isEmpty()) {
            DataTaskMappingItemVo dataTaskMappingItemVo = dataTaskMappingItemVoList.get(0);
            toField = dataTaskMappingItemVo.getToField();
            fieldDescription = dataTaskMappingItemVo.getFieldDescription();
        }
        // 当前目标子表数据
        List<Map<String, Object>> toTableMap = SqlBuilder.select(currentDbType(), model.getSubBranch().getToTable())
                .eq(model.getSubBranch().getToAssociation(), toMainId)
                .eq("del_flag", 0)
                .executeSelectList();
        if (toTableMap.isEmpty() || toTableMap.size() < 0) {
            throw new Exception("所查的目标数据不存在！");
        }
        // 收集原主表编号
        Set<Long> fromMainIdSet = new HashSet<>();
        toTableMap.forEach(toMap -> fromMainIdSet.add((Long)toMap.get(model.getSubBranch().getToTableIdColumn())));
        //查原表的商品数据
        List<Map<String, Object>> fromTableMap = SqlBuilder.select(currentDbType(), model.getSubBranch().getFromTable())
                .in(model.getSubBranch().getFromAssociation(), new ArrayList<>(fromMainIdSet)).eq("del_flag", 0).executeSelectList();
        if (fromTableMap.isEmpty()) {
            throw new RuntimeException("所选原表数据发生变更或不存在！");
        }
        List<Long> newLogIds = new ArrayList<>();
        for (Map<String, Object> toMap : toTableMap) {
            Map<String, Object> fromMap = findPullAssociationMatchFrom(fromTableMap, toMap, model);
            if (fromMap == null) {
//                throw new Exception("所选源数据在操作期间发生变更！源子表记录未找到！" + fieldDescription + "为:" + map.get(toField));
                throw new Exception("所选源数据在操作期间发生变更！源子表记录未找到！目标表id为："+toMap.get("id"));
            }
            Number toTableNumber = null;
            if (model.getType().equals("3")) {
                //下游勾稽关联
                toTableNumber = this.crossCheckAssociation(fromMap, toMap, model, fieldDescription, toField);
            } else if (model.getType().equals("1")) {
                //整单下推关联
                toTableNumber = pushDownAssociation(fromMap, toMap, model);
            }
            // 生成log表日志
            PushDownRequestPo requestPo = new PushDownRequestPo();
            requestPo.setDataTaskId(dataTaskId);
            requestPo.setToMainId(toMainId);

            newLogIds.add(writePullLog(requestPo, opId, userId, toMap.get(model.getSubBranch().getToTableIdColumn()), fromMap, toTableNumber, toMap,
            Long.parseLong(toMap.get("id").toString())));
        }
        // 查历史记录表数据
        List<DataTaskLogs> dataTaskLogsList = dataTaskLogsMapper.selectList(
                Wrappers.<DataTaskLogs>query()
                        .eq("data_task_id", dataTaskId)
                        .eq("to_main_id", toMainId)
                        .in("to_sub_id", toTableMap.stream()
                                .map(map -> (Long) map.get("id"))
                                .filter(Objects::nonNull)
                                .collect(Collectors.toList()))
                        .notIn("id", newLogIds)
                        .eq("cancelled", 0));
        if (dataTaskLogsList != null && !dataTaskLogsList.isEmpty()) {
            throw new Exception("该商品勾稽关系已存在,无需重复关联");
//            if (StringUtils.hasText(fieldDescription) && StringUtils.hasText(toField)) {
//                throw new Exception("该商品勾稽关系已存在,无需重复关联,商品" + fieldDescription + "为:" + record.get(toField));
//            } else {
//                throw new Exception("该商品勾稽关系已存在,无需重复关联");
//            }
        }
    }

    private Map<String, Object> findPullAssociationMatchFrom(final List<Map<String, Object>> fromTableMap, final Map<String, Object> toMap, final DataTaskRuntimeModel model) {
        return ListUtils.find(fromTableMap,
                fromMap ->
                        Objects.equals(toMap.get(model.getSubBranch().getToTableIdColumn()), fromMap.get(model.getSubBranch().getFromAssociation()))
                        && NumberUtil.compareTo(getFromCrossCheck(fromMap, model.getSubBranch()), toMap.get(model.getSubBranch().getFirstToTableCrossCheckFields())) >= 0
//                        && Objects.equals(getFromCrossCheck(fromMap, model.getSubBranch()), toMap.get(model.getSubBranch().getFirstToTableCrossCheckFields()))
//                        && Objects.equals(fromMap.get("price"), toMap.get("price"))
//                        && Objects.equals(fromMap.get("product_name"), toMap.get("product_name"))
                && Objects.equals(fromMap.get("sku"), toMap.get("sku"))
        );
    }

    private Object getFromCrossCheck(Map<String, Object> fromMap, DataTaskBranchVo subBranch) {
        if (StringUtils.hasText(subBranch.getFromTableCrossBackFields())) {
            if (fromMap.get(subBranch.getFromTableCrossCheckFields()) == null) {
                return fromMap.get(subBranch.getFromTableCrossBackFields());
            }
        }
        return fromMap.get(subBranch.getFromTableCrossCheckFields());
    }


    private Map<String, String> getFromTableId(List<Map<String, Object>> map, DataTaskRuntimeModel model, Long toMainId) throws Exception {
        Map<String, String> mapOne = new HashMap<>();
        for (Map<String, Object> e : map) {
            Object object = e.get(model.getSubBranch().getToTableIdColumn());
            if (object == null) {
                Map<String, Object> toMainMap = SqlBuilder.select(currentDbType(), model.getMainBranch().getToTable()).eq("id", toMainId).eq("del_flag", 0).executeSelect();
                mapOne.put(e.get("id").toString(),toMainMap.get(model.getMainBranch().getToTableIdColumn()) == null ? "" : toMainMap.get(model.getMainBranch().getToTableIdColumn()).toString());
            } else {
                mapOne.put(e.get("id").toString(),e.get(model.getSubBranch().getToTableIdColumn()) == null ? "" : e.get(model.getSubBranch().getToTableIdColumn()).toString());
            }
        }
        return mapOne;
    }

    // 下游勾稽导入关联
    private Number crossCheckAssociation(Map<String, Object> fromMap, Map<String, Object> toMap, DataTaskRuntimeModel model,String fieldDescription,String toField) throws Exception{
        // 取出原表可操作数量
        Number fromTableNumber = (Number) fromMap.get(model.getSubBranch().getFromTableCrossCheckFields());
        boolean fromNumberNull = false;
        if (fromTableNumber == null) {
            fromNumberNull = true;
            fromTableNumber = (Number) fromMap.get(model.getSubBranch().getFromTableCrossBackFields());
        }

        // 取出目标表存入的商品的数量
        Number toTableNumber = (Number) toMap.get(model.getSubBranch().getFirstToTableCrossCheckFields());
        if (fromTableNumber != null && NumberUtil.compareTo(new BigDecimal(fromTableNumber.toString()).abs(), new BigDecimal(toTableNumber.toString()).abs()) == -1) {
            if (StringUtils.hasText(fieldDescription) && StringUtils.hasText(toField)) {
                throw new Exception("所导入的商品数量不足,关联失败! 商品" + fieldDescription + "为:" + fromMap.get(toField));
            } else {
                throw new Exception("所导入的商品数量不足,关联失败! 目标表表体id为:"+toMap.get("id").toString()+"品名为："+toMap.get("product_name")+",原表表体id为:"+fromMap.get("id").toString());
            }
        }
        Map<String, Object> backDetail = new HashMap<>();
        Map<String, Object> targetDetail = new HashMap<>();
        Number subtract = NumberUtil.subtract(fromTableNumber, toTableNumber);
        backDetail.put(model.getSubBranch().getFromTableCrossCheckFields(), subtract);

        fromMap.put(model.getSubBranch().getFromTableCrossCheckFields(), subtract);

        if (StringUtils.hasText(model.getSubBranch().getToTableBdIdColumn())) {
            targetDetail.put(model.getSubBranch().getToTableBdIdColumn(), fromMap.get("id"));
        }

        if (StringUtils.hasText(model.getSubBranch().getFromTableCumulativeFields())) {
            Number fromTableCumlativeNumber = (Number) fromMap
                    .get(model.getSubBranch().getFromTableCumulativeFields());
            if (fromTableCumlativeNumber == null) {
                fromTableCumlativeNumber = 0;
            }
            Number add = NumberUtil.add(fromTableCumlativeNumber, toTableNumber);
            backDetail.put(model.getSubBranch().getFromTableCumulativeFields(), add);
        }
        // 存入原表 修改原表数据
        SqlBuilder update = SqlBuilder.update(currentDbType(), model.getSubBranch().getFromTable());
        // 更新记录原数量，作为同步版本安全识别
        update.setMap(backDetail);
        if (fromNumberNull) {
            update.eq(model.getSubBranch().getFromTableCrossBackFields(), fromMap.get(model.getSubBranch().getFromTableCrossBackFields()));
        } else {
            update.eq(model.getSubBranch().getFromTableCrossCheckFields(), fromTableNumber);
        }
        update.eq("id", fromMap.get("id"));
        if (update.executeUpdate() != 1) {
            throw new Exception("修改原表数据失败!");
        }

        if (targetDetail.size() > 0) {
            if (SqlBuilder.update(currentDbType(), model.getSubBranch().getToTable())
                    .eq("id", toMap.get("id"))
                    .setMap(targetDetail)
                    .executeUpdate() != 1) {
                throw new Exception("更新目标表数据失败！");
            }
        }

        return toTableNumber;
    }

    //整单下推导入关联
    private Number pushDownAssociation(Map<String, Object> record,Map<String, Object> map,DataTaskRuntimeModel model) throws Exception{
        Map<String,Object> backDetailMap = new HashMap<>();//整单下推关联
        // 取出原表可操作数量
        Number fromTableNumber = (Number) record.get(model.getSubBranch().getFromTableCrossCheckFields());
        if (fromTableNumber == null) {
            fromTableNumber = 0;
            backDetailMap.put(model.getSubBranch().getFromTableCrossCheckFields(),fromTableNumber);
        }
        Number toTableNumber = (Number) map.get(model.getSubBranch().getFirstToTableCrossCheckFields());
        backDetailMap.put(model.getSubBranch().getFromTableCumulativeFields(),toTableNumber);
        backDetailMap.put(model.getSubBranch().getFromTableCrossBackFields(),toTableNumber);
        // 存入原表 修改原表数据
        SqlBuilder update = SqlBuilder.update(currentDbType(), model.getSubBranch().getFromTable());
        update.setMap(backDetailMap);
        update.eq("id", record.get("id"));
        if (update.executeUpdate() != 1) {
            throw new Exception("修改原表数据失败!");
        }
        return toTableNumber;
    }

    @Override
    public Boolean getPullData(Long dataTaskId, Long fromMainId) throws Exception {
        try {
            final Long userId = currentUserId();
            // 查任务表
            final DataTaskRuntimeModel model = getModelById(dataTaskId);
            if (model == null) {
                throw new Exception("指定的配置不存在！");
            }
            // 查历史记录表数据
            List<DataTaskLogs> dataTaskLogsList = dataTaskLogsMapper.selectList(
                    Wrappers.<DataTaskLogs>query()
                            .eq("data_task_id", dataTaskId)
                            .eq("from_main_id", fromMainId)
                            .orderByDesc("create_time"));
            if (dataTaskLogsList != null) {
                List<DataTaskLogs> taskLogsList = dataTaskLogsList.stream().filter(item -> item.getCancelled() != true)
                        .collect(Collectors.toList());
                for (DataTaskLogs dataTaskLogs : taskLogsList) {
                    boolean flag = SqlBuilder.select(currentDbType(), model.getSubBranch().getToTable())
                            .eq("del_flag", 0)
                            .eq("id", dataTaskLogs.getToSubId())
                            .executeSelectExists();
                    return flag;
                }
            } else {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public Boolean getExistToNum(Long dataTaskId, Long toMainId) throws Exception {
        final Long userId = currentUserId();
        // 查任务表
        final DataTaskRuntimeModel model = getModelById(dataTaskId);
        if (model == null) {
            throw new Exception("指定的配置不存在！");
        }
        // 查历史记录表数据
        List<DataTaskLogs> dataTaskLogs = dataTaskLogsMapper.selectList(
                Wrappers.<DataTaskLogs>query()
                        .eq("data_task_id", dataTaskId)
                        .eq("to_main_id", toMainId)
                        .eq("cancelled", 0)
                        .orderByDesc("create_time"));
        if (dataTaskLogs != null && dataTaskLogs.size() > 0) {
            // 查询是否存在目标表数据
            boolean flag = SqlBuilder.select(currentDbType(), model.getSubBranch().getToTable())
                    .in("id", dataTaskLogs.stream().map(DataTaskLogs::getToSubId).collect(Collectors.toList()))
                    .eq("del_flag", 0)
                    .executeSelectExists();
            return flag;
        } else {
            return false;
        }
    }

    private void pullInsert(PushDownRequestPo requestPo, Long opId, Long userId, DataTaskRuntimeModel model,
                            Map<String, Object> overrideVars, Object mainDataId, PushDownRequestPo.PushDownEntity child,
                            Map<String, Object> record, Number toTableCrossCheck) throws Exception {
        // 取界面原可操作数量
        Number uiFromTableCrossCheck = (Number) child.getFromMap().get("_fromTableCrossCheckFields");
        if (uiFromTableCrossCheck == null) {
            throw new RException("操作异常，请求缺少关键信息");
        }
        // 取原表可操作数量
        boolean useBackNumberCheck = false;
        Number fromTableCrossCheck = (Number) record.get(model.getSubBranch().getFromTableCrossCheckFields());
        if (fromTableCrossCheck == null && StringUtils.hasText(model.getSubBranch().getFromTableCrossBackFields())) {
            useBackNumberCheck = true;
            fromTableCrossCheck = (Number) record.get(model.getSubBranch().getFromTableCrossBackFields());
        }
        if (fromTableCrossCheck == null ||
                NumberUtil.compareTo(fromTableCrossCheck, uiFromTableCrossCheck) != 0) {
            throw new RException("所选源数据在操作期间发生变更！(ID:" + record.get("id") + ")操作数量不足");
        }

        if (!Boolean.TRUE.equals(model.getSubBranch().getSupportBeyond())) {
            if (Boolean.TRUE.equals(model.getSubBranch().getSupportNegative())) {
                // 源数量为负数
                if (NumberUtil.compareTo(fromTableCrossCheck, 0) < 0 && NumberUtil.compareTo(fromTableCrossCheck, toTableCrossCheck) > 0) {
                    throw new RException("所选源数据在操作期间发生变更！(ID:" + record.get("id") + ")操作数量不足");
                }
            } else {
                if (NumberUtil.compareTo(fromTableCrossCheck, toTableCrossCheck) < 0) {
                    throw new RException("所选源数据在操作期间发生变更！(ID:" + record.get("id") + ")操作数量不足");
                }
            }
        }

        Map<String, Object> backDetail = new HashMap<>();
        Map<String, Object> toDetail = new HashMap<>(child.getToMap());
        Long toSubId = (Long) identifierGenerator.nextId(null);
        toDetail.put("id", toSubId);
        toDetail.put(model.getSubBranch().getToAssociation(), requestPo.getToMainId());
        if (StringUtils.hasText(model.getSubBranch().getToTableIdColumn())) {
            toDetail.put(model.getSubBranch().getToTableIdColumn(), mainDataId);
        }
        Number finalCrossCheck = null;
//        if(Boolean.TRUE.equals(model.getSubBranch().getIsHCCrossChecking())){
//            finalCrossCheck =  NumberUtil.add(fromTableCrossCheck, toTableCrossCheck);
//            backDetail.put(model.getSubBranch().getFromTableCrossCheckFields(), finalCrossCheck);
//            // 回写累计数
//            if (StringUtils.hasText(model.getSubBranch().getFromTableCumulativeFields())) {
//                Number fromTableCumulativeFields = (Number) record.get(model.getSubBranch().getFromTableCumulativeFields());
//                if (fromTableCumulativeFields == null) {
//                    fromTableCumulativeFields = 0;
//                }
//                backDetail.put(model.getSubBranch().getFromTableCumulativeFields(),new BigDecimal(toTableCrossCheck.toString()).abs().add(new BigDecimal(fromTableCumulativeFields.toString())));
//            }
//        }else {
        finalCrossCheck =  NumberUtil.subtract(fromTableCrossCheck, toTableCrossCheck);
        backDetail.put(model.getSubBranch().getFromTableCrossCheckFields(), finalCrossCheck);
        // 回写累计数
        if (StringUtils.hasText(model.getSubBranch().getFromTableCumulativeFields())) {
            Number fromTableCumulativeFields = (Number) record.get(model.getSubBranch().getFromTableCumulativeFields());
            if (fromTableCumulativeFields == null) {
                fromTableCumulativeFields = 0;
            }
            backDetail.put(model.getSubBranch().getFromTableCumulativeFields(), NumberUtil.add(toTableCrossCheck, fromTableCumulativeFields));
        }
//        }
        // 回写状态
        if (StringUtils.hasText(model.getSubBranch().getFromTableStatusField())) {
            if (Boolean.TRUE.equals(model.getSubBranch().getSupportNegative())) {
                backDetail.put(model.getSubBranch().getFromTableStatusField(), NumberUtil.compareTo(finalCrossCheck, 0) != 0 ? model.getSubBranch().getMedian() : model.getSubBranch().getCompletionValue());
            } else {
                backDetail.put(model.getSubBranch().getFromTableStatusField(), NumberUtil.compareTo(finalCrossCheck, 0) > 0 ? model.getSubBranch().getMedian() : model.getSubBranch().getCompletionValue());
            }
        }

        // 红冲操作，反向写入目标表的数量值
        if (Boolean.TRUE.equals(model.getSubBranch().getIsHCCrossChecking())) {
            toDetail.put(model.getSubBranch().getFirstToTableCrossCheckFields(), NumberUtil.multiply(toTableCrossCheck, -1));
        }

        overrideVars.put("numChange", toTableCrossCheck);
        handleMappings(model.getSubBranch(), record, backDetail, toDetail, child.getToMap(), overrideVars);

        // 多目标数量字段处理
        for (String otherToTableCrossCheckField : model.getSubBranch().getOtherToTableCrossCheckFields()) {
            toDetail.put(otherToTableCrossCheckField, toTableCrossCheck);
        }

        // 新增子表
        SqlBuilder insertRecordBuilder = SqlBuilder.insert(currentDbType(), model.getSubBranch().getToTable());
        insertRecordBuilder.setMapSkipNullValue(toDetail);
        insertRecordBuilder.executeInsert();

        // 写日志
        writePullLog(requestPo, opId, userId, mainDataId, record, toTableCrossCheck, toDetail, toSubId);

        // 回写
        SqlBuilder updateFromBuilder = SqlBuilder.update(currentDbType(), model.getSubBranch().getFromTable());
        updateFromBuilder.setMap(backDetail);
        // 更新记录 id
        updateFromBuilder.eq("id", child.getFromMap().get("id"));
        // 更新记录原数量，作为同步版本安全识别
        if (useBackNumberCheck) {
            updateFromBuilder.eq(model.getSubBranch().getFromTableCrossBackFields(), uiFromTableCrossCheck);
        } else {
            updateFromBuilder.eq(model.getSubBranch().getFromTableCrossCheckFields(), uiFromTableCrossCheck);
        }
        if (updateFromBuilder.executeUpdate() != 1) {
            throw new RException("回写更新源子表异常，源数据可能在操作期间已更新");
        }
    }

    private Long writePullLog(PushDownRequestPo requestPo, Long opId, Long userId, Object fromMainId,
                              Map<String, Object> fromRow, Number toTableCrossCheck, Map<String, Object> toRow, Long toSubId) {
        DataTaskLogs logs = new DataTaskLogs();
        logs.setId((Long) identifierGenerator.nextId(null));
        logs.setOpId(opId);
        logs.setDataTaskId(requestPo.getDataTaskId());
        logs.setType(DataTaskLogs.TYPE_PULL);
        logs.setFromMainId(NumberUtil.toLong(fromMainId));
        logs.setToMainId(requestPo.getToMainId());
        logs.setFromSubId(NumberUtil.toLong(fromRow.get("id")));
        logs.setToSubId(toSubId);
        logs.setNumChange(NumberUtil.toDecimal(toTableCrossCheck));
        logs.setFromRow(fromRow);
        logs.setToRow(toRow);
        logs.setCreateTime(new Date());
        logs.setCreateUser(userId);
        logs.setCancelled(false);
        writeTaskLog(logs);
        return logs.getId();
    }

    /**
     * 按日志更新
     *
     * @param requestPo         请求信息
     * @param opId
     * @param userId            操作用户
     * @param model             数据任务运行模型
     * @param oldTaskLog        历史日志
     * @param fromRecord        源数据
     * @param entity            勾稽用户输入主数据
     * @param child             勾稽用户输入子数据
     * @param toTableCrossCheck 本次用户输入的勾稽数
     * @param overrideVars      表达式重写变量
     * @throws Exception 异常
     */
    private void pullUpdate(PushDownRequestPo requestPo, Long opId, Long userId, DataTaskRuntimeModel model,
                            DataTaskLogs oldTaskLog, Map<String, Object> fromRecord, PushDownRequestPo.PushDownEntity entity,
                            PushDownRequestPo.PushDownEntity child, Number toTableCrossCheck, Map<String, Object> overrideVars)
            throws Exception {
        Number currentFromTableCrossCheckFields = (Number) fromRecord.get(model.getSubBranch().getFromTableCrossCheckFields());
        Number backFromTableCrossCheckFields = null;
        Number numOffset = null;
//        if(model.getSubBranch().getIsHCCrossChecking()){
//            // 去掉历史的原可用数量
//            backFromTableCrossCheckFields = NumberUtil.add(currentFromTableCrossCheckFields, oldTaskLog.getNumChange().abs());
//            // 相对历史的数量变更量
//            numOffset = NumberUtil.subtract(toTableCrossCheck, oldTaskLog.getNumChange().abs());
//        }else{
        // 去掉历史的原可用数量
        backFromTableCrossCheckFields = NumberUtil.add(currentFromTableCrossCheckFields, oldTaskLog.getNumChange());
        // 相对历史的数量变更量
        numOffset = NumberUtil.subtract(toTableCrossCheck, oldTaskLog.getNumChange());
//        }
        // 取目标子表对应记录
        final Map<String, Object> toRecord = SqlBuilder.select(currentDbType(), model.getSubBranch().getToTable())
                .eq("del_flag", false)
                .eq("id", oldTaskLog.getToSubId())
                .executeSelect();
        if (toRecord == null) {
            throw new RException("修改异常，目标子表数据未找到！");
        }
        Number currentToTableCrossCheckFields = (Number) toRecord
                .get(model.getSubBranch().getFirstToTableCrossCheckFields());

        if (Boolean.TRUE.equals(model.getSubBranch().getIsHCCrossChecking())) {
            if (NumberUtil.compareTo(NumberUtil.multiply(currentToTableCrossCheckFields, -1), oldTaskLog.getNumChange()) < 0) {
                throw new RException(String.format(
                        "修改异常，源ID:%s 目标ID:%s 历史勾稽数量为 %s，现有目标表数量为 %s，无法回退！",
                        oldTaskLog.getFromSubId(),
                        oldTaskLog.getToSubId(),
                        oldTaskLog.getNumChange(),
                        currentToTableCrossCheckFields
                ));
            }
        } else {
            if (NumberUtil.compareTo(currentToTableCrossCheckFields, oldTaskLog.getNumChange()) < 0) {
                throw new RException(String.format(
                        "修改异常，源ID:%s 目标ID:%s 历史勾稽数量为 %s，现有目标表数量为 %s，无法回退！",
                        oldTaskLog.getFromSubId(),
                        oldTaskLog.getToSubId(),
                        oldTaskLog.getNumChange(),
                        currentToTableCrossCheckFields
                ));
            }
        }

        // -------------- 更新源表 -------------
        Map<String, Object> updateFromMap = new HashMap<>();
        // 加回勾稽数量
        updateFromMap.put(model.getSubBranch().getFromTableCrossCheckFields(),
                NumberUtil.subtract(backFromTableCrossCheckFields, toTableCrossCheck));
        fromRecord.put(model.getSubBranch().getFromTableCrossCheckFields(),
                updateFromMap.get(model.getSubBranch().getFromTableCrossCheckFields()));
        // 减累计数量
        if (StringUtils.hasText(model.getSubBranch().getFromTableCumulativeFields())) {
            updateFromMap.put(model.getSubBranch().getFromTableCumulativeFields(), NumberUtil
                    .add((Number) fromRecord.get(model.getSubBranch().getFromTableCumulativeFields()), numOffset));
            fromRecord.put(model.getSubBranch().getFromTableCumulativeFields(),
                    updateFromMap.get(model.getSubBranch().getFromTableCumulativeFields()));
        }
        // 恢复中间状态
        if (StringUtils.hasText(model.getSubBranch().getFromTableStatusField())
                && StringUtils.hasText(model.getSubBranch().getMedian())) {
            updateFromMap.put(model.getSubBranch().getFromTableStatusField(), model.getSubBranch().getMedian());
            fromRecord.put(model.getSubBranch().getFromTableStatusField(),
                    updateFromMap.get(model.getSubBranch().getFromTableStatusField()));
        }
        // 如果同时存在 源表钩稽字段 和 源表回退字段 而且 当前两个值相同（不为空）将 源表钩稽字段 更新为空
        if (StringUtils.hasText(model.getSubBranch().getFromTableCrossCheckFields()) &&
                StringUtils.hasText(model.getSubBranch().getFromTableCrossBackFields()) &&
                updateFromMap.get(model.getSubBranch().getFromTableCrossCheckFields()) != null &&
                updateFromMap.get(model.getSubBranch().getFromTableCrossCheckFields()).equals(fromRecord.get(model.getSubBranch().getFromTableCrossBackFields()))
        ) {
            updateFromMap.put(model.getSubBranch().getFromTableCrossCheckFields(), null);
        }

        if (SqlBuilder.update(currentDbType(), model.getSubBranch().getFromTable())
                .setMap(updateFromMap)
                .eq("id", oldTaskLog.getFromSubId())
                .eq(model.getSubBranch().getFromTableCrossCheckFields(), currentFromTableCrossCheckFields)
                .executeUpdate() != 1) {
            throw new RException("更新数据发生异常，源数据在操作期间发生改变！");
        }

        // ---------- 更新目标表 ------------
        Map<String, Object> updateToMap = new HashMap<>();
        if (Boolean.TRUE.equals(model.getSubBranch().getIsHCCrossChecking())) {
            currentToTableCrossCheckFields = NumberUtil.multiply(currentToTableCrossCheckFields, -1);
        }
        Number newToTableCrossCheck = NumberUtil
                .add(NumberUtil.subtract(currentToTableCrossCheckFields, oldTaskLog.getNumChange()), toTableCrossCheck);
        if (NumberUtil.compareTo(newToTableCrossCheck, 0) == 0) {
            // 新的数量是 0
            updateToMap.put("del_flag", true);
        } else {
            // 数量变更
            if (Boolean.TRUE.equals(model.getSubBranch().getIsHCCrossChecking())) {
                updateToMap.put(model.getSubBranch().getFirstToTableCrossCheckFields(), NumberUtil.multiply(newToTableCrossCheck, -1));
            } else {
                updateToMap.put(model.getSubBranch().getFirstToTableCrossCheckFields(), newToTableCrossCheck);
            }
            // 多目标数量字段处理
            for (String otherToTableCrossCheckField : model.getSubBranch().getOtherToTableCrossCheckFields()) {
                updateToMap.put(otherToTableCrossCheckField, updateToMap.get(model.getSubBranch().getFirstToTableCrossCheckFields()));
            }
            overrideVars.put("numChange",toTableCrossCheck);
            handleMappings(model.getSubBranch(), fromRecord, updateFromMap, updateToMap, entity.getToMap(), overrideVars);

            // 写新历史
            writePullLog(requestPo, opId, userId, oldTaskLog.getFromMainId(), fromRecord, newToTableCrossCheck,
                    toRecord, oldTaskLog.getToSubId());
        }

        if (Boolean.TRUE.equals(model.getSubBranch().getIsHCCrossChecking())) {
            currentToTableCrossCheckFields = NumberUtil.multiply(currentToTableCrossCheckFields, -1);
        }
        if (SqlBuilder.update(currentDbType(), model.getSubBranch().getToTable())
                .setMap(updateToMap)
                .eq("id", oldTaskLog.getToSubId())
                .eq(model.getSubBranch().getFirstToTableCrossCheckFields(), currentToTableCrossCheckFields)
                .executeUpdate() != 1) {
            throw new RException("更新数据发生异常，目标数据在操作期间发生改变！");
        }

        // 更新取消历史
        cancellationTaskLog(oldTaskLog.getId(), userId);
    }

    private void pushDownEntity(DataTaskRuntimeModel model, PushDownRequestPo.PushDownEntity entity) throws Exception {
        if (model.getSubBranch() == null) {
            throw new RException("暂未实现单表下推处理");
        }
        HandleContext context = new HandleContext();
        context.setUserId(currentUserId());
        context.setOpId((Long) identifierGenerator.nextId(null));
        Map<String, Object> overrideVars = new HashMap<>();
        overrideVars.put(VAR_CURRENT_USER_ID, context.getUserId());
        Object mainDataId = entity.getFromMap().get("id");
        final Map<String, Object> mainTable = getMainFromTable(model, model.getMainBranch(), mainDataId);
        if (mainTable == null) {
            throw new RException("未找到主来源记录！");
        }
        if (!isCanPushDown(model.getMainBranch(), mainTable)) {
            throw new RException("当前主来源记录状态不可运行下推！");
        }
        context.setMainSourceTo(
                new HandleSourceTo()
                        .setContext(context)
                        .setFromMap(mainTable)
                        .setToMap(new HashMap<>())
                        .setBranch(model.getMainBranch())
                        .setReturnFormMap(new HashMap<>()));
        List<Map<String, Object>> records = SqlBuilder.select(currentDbType(), model.getSubBranch().getFromTable())
                .in("id", entity.getChildren().stream().map(e -> e.getFromMap().get("id")))
                .executeSelectList();
        if (records.size() != entity.getChildren().size()) {
            throw new RException("所选下推数据在操作期间发生变更！");
        }
        List<Map<String, Object>> inputDetails = new ArrayList<>();
        for (Map<String, Object> record : records) {
            entity.getChildren().stream().filter(c -> c.getFromMap().get("id").equals(record.get("id").toString()))
                    .findFirst()
                    .ifPresentOrElse(c -> inputDetails.add(c.getToMap()), () -> inputDetails.add(null));
        }
        context.setDetailSourceToList(pushDownRecords(
                records,
                inputDetails,
                model.getSubBranch(),
                overrideVars,
                rawFromMap -> {
                    final PushDownRequestPo.PushDownEntity recordEntity = entity.getChildren().stream()
                            .filter(c -> new BigDecimal(c.getFromMap().get("id").toString())
                                    .compareTo(new BigDecimal(rawFromMap.get("id").toString())) == 0)
                            .findFirst().get();
                    return NumberUtil.asNumber(recordEntity.getToMap().get(model.getSubBranch().getFirstToTableCrossCheckFields()));
                }, null));

        contextEndHandle(model, context, entity.getToMap());
        afterPushDown(model, context.getTraceMap());
    }

    private void afterPushDown(DataTaskRuntimeModel model, Object entity) throws Exception {
        if (StringUtils.hasText(model.getInvokeAfterPushService()) && StringUtils.hasText(model.getInvokeAfterPushMethod())) {
            serviceInvoker.invoke(model.getInvokeAfterPushService(), model.getInvokeAfterPushMethod(), entity);
        }
    }

    private void afterPullUp(DataTaskRuntimeModel model, Object entity) throws Exception {
        if (StringUtils.hasText(model.getInvokeAfterPullService()) && StringUtils.hasText(model.getInvokeAfterPullMethod())) {
            serviceInvoker.invoke(model.getInvokeAfterPullService(), model.getInvokeAfterPullMethod(), entity);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void batchPushDownWholeOrder(Long dataTaskId, List<Long> fromOrderIdList) throws Exception {
        for (Long fromOrderId : fromOrderIdList) {
            pushDownWholeOrder(dataTaskId, fromOrderId);
        }
    }

    /**
     * 标准化记录数据
     *
     * <ul>
     *     <li>LocalDateTime 格式化为字符串</li>
     * </p>
     * @param records 记录数据
     */
    private void normalizeRecords(List<Map<String, Object>> records) {
        for (Map<String, Object> record : records) {
            for (Map.Entry<String, Object> stringObjectEntry : record.entrySet()) {
                if (stringObjectEntry.getValue() instanceof LocalDateTime) {
                    LocalDateTime localDateTime = (LocalDateTime)stringObjectEntry.getValue();
                    record.put(stringObjectEntry.getKey(), LocalDateTimeUtil.format(localDateTime, "yyyy-MM-dd HH:mm:ss"));
                }
            }
        }
    }

    @Override
    public Page<Map<String, Object>> fromDataSearchPage(Long dataTaskId, Long mainDataId, FromDataPageSearch pageSearch)
            throws Exception {
        final DataTaskRuntimeModel model = getModelById(dataTaskId);
        final Map<String, Object> mainTable = getMainFromTable(model, model.getMainBranch(), mainDataId, true);
        if (mainTable == null) {
            throw new RException("未找到主来源记录！");
        }
        if (model.getSubBranch() != null) {
            // 判断主表是否满足下推条件
            if (!isCanPushDown(model.getMainBranch(), mainTable)) {
                throw new RException("当前主来源记录状态不可运行下推！");
            }
        }

        final DataTaskBranchVo branch = model.getSubBranch() != null ? model.getSubBranch() : model.getMainBranch();

        Page<Map<String, Object>> page = Page.of(pageSearch.getCurrent(), pageSearch.getPageSize(), 0);
        // 查总记录数
        Long count = SqlBuilder.select(currentDbType(), branch.getFromTable())
                .eq("del_flag", false)
                .eq(model.getSubBranch().getFromAssociation(),
                        mainTable.get(model.getMainBranch().getFromAssociation()))
                .apply(sb -> fromDataSearchNewApply(sb, pageSearch, branch))
                .executeSelectCount();
        page.setTotal(count);
        if (count == 0 || count <= (pageSearch.getCurrent() - 1) * pageSearch.getPageSize()) {
            return page;
        }

        List<Map<String, Object>> records = SqlBuilder.select(currentDbType(), branch.getFromTable())
                .eq("del_flag", false)
                .eq(model.getSubBranch().getFromAssociation(),
                        mainTable.get(model.getMainBranch().getFromAssociation()))
                .apply(b -> appendMappingSelectJoin(b, branch))
                .apply(sb -> fromDataSearchNewApply(sb, pageSearch, branch))
                .skip((pageSearch.getCurrent() - 1) * pageSearch.getPageSize())
                .limit(pageSearch.getPageSize())
                .executeSelectList();

        // 源表数量字段到源表钩稽字段处理
        if (StringUtils.hasText(branch.getFromTableCrossBackFields())) {
            for (Map<String, Object> record : records) {
                if (Objects.isNull(record.get(branch.getFromTableCrossCheckFields()))) {
                    record.put(branch.getFromTableCrossCheckFields(), record.get(branch.getFromTableCrossBackFields()));
                }
            }
        }

        // 将主表数据加入到详情数据中
        records.forEach(map -> {
            for (Map.Entry<String, Object> mainKeyValue : mainTable.entrySet()) {
                map.put(String.format("main.%s", mainKeyValue.getKey()), mainKeyValue.getValue());
            }
        });

        normalizeRecords(records);

        page.setRecords(records);

        return page;
    }

    private void fromDataSearchNewApply(SqlBuilder sb, FromDataPageSearch pageSearch, DataTaskBranchVo branch) {
        if (pageSearch.getExcludeChildIds() != null && !pageSearch.getExcludeChildIds().isEmpty()) {
            sb.notIn("id", pageSearch.getExcludeChildIds());
        }
        if (Boolean.TRUE.equals(pageSearch.getExcludeNotNum())) {
            String checkField = branch.getFromTableCrossCheckFields();
            String backField = branch.getFromTableCrossBackFields();
            if (StringUtils.hasText(backField)) {
                List<SqlBuilder.ConditionPart> backNumConditionParts = new ArrayList<>();
                backNumConditionParts.add(new SqlBuilder.ConditionPart()
                        .setColumnName(checkField)
                        .setConditionKind(SqlBuilder.ConditionKind.IsNull)
                );
                backNumConditionParts.add(new SqlBuilder.ConditionPart()
                        .setColumnName(backField)
                        .setConditionKind(SqlBuilder.ConditionKind.GreaterThan)
                        .setConditionValue(0)
                );
                List<SqlBuilder.ConditionPart> orGroupParts = new ArrayList<>();
                orGroupParts.add(new SqlBuilder.ConditionPart()
                        .setConditionKind(SqlBuilder.ConditionKind.AndGroup)
                        .setConditionValue(backNumConditionParts)
                );
                orGroupParts.add(new SqlBuilder.ConditionPart()
                        .setColumnName(checkField)
                        .setConditionKind(SqlBuilder.ConditionKind.GreaterThan)
                        .setConditionValue(0)
                );
                sb.getConditionParts().add(new SqlBuilder.ConditionPart().setConditionKind(SqlBuilder.ConditionKind.OrGroup).setConditionValue(orGroupParts));
            } else {
                sb.gt(checkField, 0);
            }
        }
        if (pageSearch.getQueryParams() != null && pageSearch.getQueryParams().length() > 2 && pageSearch.getQueryParams().startsWith("{") && pageSearch.getQueryParams().endsWith("}")) {
            JSONObject queryParams = (JSONObject)JSONObject.parse(pageSearch.getQueryParams());
            for (String key : queryParams.keySet()) {
                CharSequence column = key;
                DataTaskMappingItemVo mapping = ListUtils.find(branch.getMappings(), m -> m.getTypeValue() != null && Objects.equals(m.getTypeValue().getColumnName(), key));
                if (mapping != null) {
                    if(mapping.getDisplayOpt() != null && StringUtils.hasText(mapping.getDisplayOpt().getSelectSql())) {
                        if (StringUtils.hasText(mapping.getDisplayOpt().getJoinSql()) && !existsJoinSql(sb, mapping.getDisplayOpt().getJoinSql())) {
                            sb.getJoinBuilders().add(new SqlBuilder.JoinBuilder()
                                    .setJoinKind(SqlBuilder.JoinKind.Sql)
                                    .setSql(mapping.getDisplayOpt().getJoinSql()));
                        }
                        column = new SqlBuilder.JoinOnConstValue(mapping.getDisplayOpt().getSelectSql());
                    }
                    else if (StringUtils.hasText(mapping.getTypeValue().getSelectSql())) {
                        if (StringUtils.hasText(mapping.getTypeValue().getJoinSql()) && !existsJoinSql(sb, mapping.getTypeValue().getJoinSql())) {
                            sb.getJoinBuilders().add(new SqlBuilder.JoinBuilder()
                                    .setJoinKind(SqlBuilder.JoinKind.Sql)
                                    .setSql(mapping.getTypeValue().getJoinSql()));
                        }
                        column = new SqlBuilder.JoinOnConstValue(mapping.getTypeValue().getSelectSql());
                    }
                }
                sb.andExp(column, likeExp(queryParams.getString(key)));
            }
        }
    }

    private boolean existsJoinSql(SqlBuilder sb, String joinSql) {
        return sb.getJoinBuilders().stream().anyMatch(jb -> jb.getJoinKind() == SqlBuilder.JoinKind.Sql && Objects.equals(jb.getSql(), joinSql));
    }

    @Override
    public Page<Map<String, Object>> toFromMainSearchPage(Long dataTaskId, Long toMainDataId, PageSearch pageSearch) throws Exception {

        final DataTaskRuntimeModel model = getModelById(dataTaskId);
        final DataTaskBranchVo branch = model.getSubBranch() != null ? model.getSubBranch() : model.getMainBranch();
        pageSearch.initFinal();
        SqlBuilder mainBuilder = SqlBuilder.select(currentDbType(), model.getMainBranch().getToTable())
                .eq("del_flag", false)
                .eq("id", toMainDataId);
        // 添加过滤条件
        applyFromFilterMappings(model.getMainBranch(), mainBuilder);

        boolean mainExists = mainBuilder.executeSelectExists();
        if (!mainExists) {
            throw new RException("勾稽目标数据不存在或当前状态不可操作！");
        }
        Page<Map<String, Object>> page = Page.of(pageSearch.getCurrent(), pageSearch.getPageSize(), 0);
        final SqlBuilder sqlBuilder = SqlBuilder.select(currentDbType(), branch.getFromTable())
                .eq("del_flag", false);
        sqlBuilder.groupBy("m.id");
        sqlBuilder.selectExpression("COUNT(t.id)", "data.records");
        applyFromFilterMappings(branch, sqlBuilder, null);
        if (StringUtils.hasText(branch.getFromTableDetectionField())) {
            // 过滤检测字段
            sqlBuilder.in(branch.getFromTableDetectionField(), List.of("Y", "1"));
        }
        String numIfExp = StringUtils.hasText(branch.getFromTableCrossBackFields()) ? sqlBuilder.getSqlHandler().funcIf("t." + branch.getFromTableCrossCheckFields() + " is null", "t." + branch.getFromTableCrossBackFields(), "t." + branch.getFromTableCrossCheckFields()) : String.format("t.%s", branch.getFromTableCrossCheckFields());
        String sumNumNullable = sqlBuilder.getSqlHandler().funcIfNull("SUM(logs.num_change)", "0");
        sqlBuilder.selectExpression(String.format("SUM(%s) + %s", numIfExp, sumNumNullable), "data.fromTableCrossCheckFields");
        sqlBuilder.selectExpression("SUM(logs.num_change)", "data.toTableCrossCheckFields");
        appendMappingFieldsJoin(sqlBuilder, branch.getMappings());

        // 过滤数量为零或历史中没有
        if(!Boolean.TRUE.equals(model.getMainBranch().getIsHCCrossChecking())){
            sqlBuilder.orGroup(orParts -> {
                List<SqlBuilder.ConditionPart> numConditionParts = new ArrayList<>();

                numConditionParts.add(new SqlBuilder.ConditionPart()
                        .setTableAsName("t")
                        .setColumnName(branch.getFromTableCrossCheckFields())
                        .setConditionKind(SqlBuilder.ConditionKind.NotEquals)
                        .setConditionValue(0));

                // 包括回退数量字段
                if(StringUtils.hasText(branch.getFromTableCrossBackFields())) {
                    List<SqlBuilder.ConditionPart> backNumConditionParts = new ArrayList<>();
                    backNumConditionParts.add(new SqlBuilder.ConditionPart()
                            .setTableAsName("t")
                            .setColumnName(branch.getFromTableCrossCheckFields())
                            .setConditionKind(SqlBuilder.ConditionKind.IsNull));
                    backNumConditionParts.add(new SqlBuilder.ConditionPart()
                            .setTableAsName("t")
                            .setColumnName(branch.getFromTableCrossBackFields())
                            .setConditionKind(SqlBuilder.ConditionKind.NotEquals)
                            .setConditionValue(0));
                    numConditionParts.add(new SqlBuilder.ConditionPart()
                            .setConditionKind(SqlBuilder.ConditionKind.AndGroup)
                            .setConditionValue(backNumConditionParts)
                    );
                }

                orParts.add(new SqlBuilder.ConditionPart()
                        .setConditionKind(SqlBuilder.ConditionKind.OrGroup)
                        .setConditionValue(numConditionParts));

                orParts.add(new SqlBuilder.ConditionPart()
                        .setTableAsName("logs")
                        .setColumnName("to_sub_id")
                        .setConditionKind(SqlBuilder.ConditionKind.IsNotNull));
            });
        }

        appendMappingSelectJoin(sqlBuilder, model.getSubBranch(), "t", false, null, true);

        if (model.getSubBranch() != null) {
            final SqlBuilder.JoinBuilder[] fromMainJoinBuilder = { null };
            // 添加源主表 JOIN 和过滤
            sqlBuilder.innerJoin(
                    model.getMainBranch().getFromTable(),
                    "m",
                    joinBuilder -> {
                        joinBuilder.onPart(model.getSubBranch().getFromAssociation(),
                                model.getMainBranch().getFromAssociation());
                        fromMainJoinBuilder[0] = joinBuilder;
                    },
                    joinSqlBuilder -> {
                        joinSqlBuilder.eq("del_flag", 0);
                        joinSqlBuilder.selectColumn("id", "id", true);
                        if (StringUtils.hasText(model.getMainBranch().getFromTableStatusField())
                                && StringUtils.hasText(model.getMainBranch().getCompletionValue())) {
                            // 过滤已完成
                            joinSqlBuilder.selectColumn(model.getMainBranch().getFromTableStatusField(),
                                    model.getMainBranch().getFromTableStatusField());
                            joinSqlBuilder.ne(model.getMainBranch().getFromTableStatusField(),
                                    model.getMainBranch().getCompletionValue());
                            sqlBuilder.groupBy("m." + model.getMainBranch().getFromTableStatusField());
                        }
                        if (StringUtils.hasText(model.getMainBranch().getFromTableDetectionField())) {
                            // 过滤检测字段
                            joinSqlBuilder.selectColumn(model.getMainBranch().getFromTableDetectionField(),
                                    model.getMainBranch().getFromTableDetectionField());
                            joinSqlBuilder.in(model.getMainBranch().getFromTableDetectionField(), List.of("Y", "1"));
                            sqlBuilder.groupBy("m." + model.getMainBranch().getFromTableDetectionField());
                        }
                        Map<String, String> mainJoinMaps = appendMappingFieldsJoin(joinSqlBuilder, model.getMainBranch().getMappings());
                        for (DataTaskMappingItemVo mapping : model.getMainBranch().getMappings()) {
                            if (DataTaskMappingTypes.FIELD.equals(mapping.getType())) {
                                String selectSql = mainJoinMaps.get(mapping.getTypeValue().getColumnName());
                                if (StringUtils.hasText(selectSql)) {
                                    joinSqlBuilder.selectExpression(selectSql,
                                            mapping.getTypeValue().getColumnName());
                                } else {
                                    joinSqlBuilder.selectColumn(mapping.getTypeValue().getColumnName(),
                                            mapping.getTypeValue().getColumnName());
                                }
                                sqlBuilder.groupBy("m." + mapping.getTypeValue().getColumnName());
                            }
                        }
                        fromDataSearchApply(joinSqlBuilder, model.getMainBranch(), pageSearch);
                    });

            Map<String, String> displaySelectJoinMap = appendMappingSelectJoin(sqlBuilder, model.getMainBranch(), "m", false, null);
            for (Map.Entry<String, String> displaySelectJoin : displaySelectJoinMap.entrySet()) {
                sqlBuilder.groupBy(displaySelectJoin.getKey());
            }

            // 关联到日志表返回
            sqlBuilder.leftJoin(
                    "data_task_logs",
                    "logs",
                    joinBuilder -> {
                        // 关联当前任务
                        joinBuilder.onPart(
                                new SqlBuilder.JoinOnConstValue(dataTaskId.toString()),
                                "data_task_id");
                        // 关联当前主表记录
                        joinBuilder.onPart("m", "id", "logs", "from_main_id");
                        // 关联当前子表记录
                        joinBuilder.onPart("t", "id", "logs", "from_sub_id");
                        // 过滤已撤消
                        joinBuilder.onPart(new SqlBuilder.JoinOnConstValue("0"), "cancelled");
                        // 当前当前目标主表记录
                        joinBuilder.onPart(
                                new SqlBuilder.JoinOnConstValue(toMainDataId.toString()),
                                "to_main_id");
                    },
                    joinSqlBuilder -> {
                        // 返回目标 id
//                        joinSqlBuilder.selectExpression("to_sub_id", "logs.to_sub_id");
                        joinSqlBuilder.selectExpression("sum(logs.num_change)", "logs.num_change");
                    });

            // 关联到目标主表做固定过滤字段处理
            sqlBuilder.innerJoin(
                    model.getMainBranch().getToTable(),
                    "tm",
                    joinBuilder -> {
                        joinBuilder.onPart(new SqlBuilder.JoinOnConstValue(toMainDataId.toString()), "id");
                        for (DataTaskMappingItemVo mapping : model.getMainBranch().getMappings()) {
                            if (DataTaskMappingTypes.FILTER.equals(mapping.getType())) {
                                String toField = mapping.getToField().trim();
                                if (toField.contains(" ")) {
                                    if (toField.matches(FILTER_TM_REGEX)) {
                                        // 引用了目标主表字段的过滤，需要加在 joinBuilder 上
                                        joinBuilder.onPart("m", mapping.getTypeValue().getColumnName(), convertFilterExpArgs(toField, toMainDataId));
                                    } else {
                                        // 包括空格的字段为只对来源主表的过滤表达式
                                        fromMainJoinBuilder[0].onPart("m", mapping.getTypeValue().getColumnName(), convertFilterExpArgs(toField, toMainDataId));
                                    }
                                } else if (mapping.getTypeValue().getColumnName().contains(" ")) {
                                    // 包括空格的源字段为只对目标主表的过滤表达式
                                    joinBuilder.onPart("tm", toField, convertFilterExpArgs(mapping.getTypeValue().getColumnName(), toMainDataId));
                                } else {
                                    // 此表对主表的过滤
                                    joinBuilder.onPart("m", mapping.getTypeValue().getColumnName(), "tm", toField);
                                }
                            }
                        }
                    },
                    joinSqlBuilder -> {
//                        joinSqlBuilder.selectColumn("id", "tm.id");
                    });

        }
        sqlBuilder.groupBy();

        final Long count = sqlBuilder.executeSelectCount();
        page.setTotal(count);
        if (count == 0 || count <= (long) (pageSearch.getCurrent() - 1) * pageSearch.getPageSize()) {
            return page;
        }
        sqlBuilder.skip((pageSearch.getCurrent() - 1) * pageSearch.getPageSize())
                .limit(pageSearch.getPageSize());
        page.setRecords(sqlBuilder.executeSelectList());

        // Back 字段的后置处理
        if (StringUtils.hasText(branch.getFromTableCrossBackFields())) {
            for (Map<String, Object> record : page.getRecords()) {
                if (record.get(branch.getFromTableCrossCheckFields()) == null) {
                    record.put(branch.getFromTableCrossCheckFields(), record.get(branch.getFromTableCrossBackFields()));
                }
            }
        }

        return page;
    }
    private void fromDataSearchApply(SqlBuilder sqlBuilder, DataTaskBranchVo branch, PageSearch pageSearch) {
        fromDataSearchApply(sqlBuilder, branch, pageSearch, false);
    }

    private void fromDataSearchApply(SqlBuilder sqlBuilder, DataTaskBranchVo branch, PageSearch pageSearch, boolean handleChild) {
        if (branch == null || branch.getMappings() == null) {
            return;
        }
        final List<DataTaskMappingItemVo> mappings = branch.getMappings();
        pageSearch.getFinalFilters().stream().filter(f -> "main.id".equals(f.getDataIndex().toFieldName())).findFirst()
                .ifPresent(f -> sqlBuilder.andExp(new SqlBuilder.JoinOnConstValue("m.id"), eqExp(f.getValue())));
        for (DataTaskMappingItemVo mapping : mappings) {
            if (DataTaskMappingTypes.FIELD.equals(mapping.getType())) {
                final List<PageSearchFilter> mappingFilters = pageSearch.getFinalFilters().stream()
                        .filter(f -> String.format(handleChild ? "child.%s" : "%s", mapping.getTypeValue().getColumnName()).equals(f.getDataIndex().toString()))
                        .collect(Collectors.toList());
                // 当前暂时只支持 contains op
                if (mappingFilters.size() > 0) {
                    final PageSearchFilter mappingFilter = mappingFilters.get(0);
                    final String value = SheetUtils.trim(mappingFilter.getValue());
                    if (StringUtils.hasText(value)) {
                        if ("range".equals(mappingFilter.getOp())) {
                            String[] range = value.split(",");
                            String startValue = range.length > 0 && StringUtils.hasText(range[0]) ? range[0] : null;
                            String endValue = range.length > 1 && StringUtils.hasText(range[1]) ? range[1] : null;
                            if (startValue != null || endValue != null) {
                                if (handleChild) {
                                    CharSequence column = new SqlBuilder.JoinOnConstValue(String.format("t.%s", mapping.getTypeValue().getColumnName()));
                                    if (startValue != null) {
                                        sqlBuilder.andExp(column, String.format(">= '%s'", startValue));
                                    }
                                    if (endValue != null) {
                                        sqlBuilder.andExp(column, String.format("<= '%s'", endValue));
                                    }
                                } else {
                                    if (startValue != null) {
                                        sqlBuilder.andExp(mapping.getTypeValue().getColumnName(), String.format(">= '%s'", startValue));
                                    }
                                    if (endValue != null) {
                                        sqlBuilder.andExp(mapping.getTypeValue().getColumnName(), String.format("<= '%s'", endValue));
                                    }
                                }
                            }
                        } else {
                            if (StringUtils.hasText(mapping.getTypeValue().getSelectSql())) {
                                sqlBuilder.andExp(new SqlBuilder.JoinOnConstValue(mapping.getTypeValue().getSelectSql()), likeExp(value));
                            } else if (mapping.getDisplayOpt() != null && StringUtils.hasText(mapping.getDisplayOpt().getSelectSql())) {
                                sqlBuilder.andExp(new SqlBuilder.JoinOnConstValue(mapping.getDisplayOpt().getSelectSql()), likeExp(value));
                            } else {
                                if (handleChild) {
                                    sqlBuilder.andExp(new SqlBuilder.JoinOnConstValue(String.format("t.%s", mapping.getTypeValue().getColumnName())), likeExp(value));
                                } else {
                                    sqlBuilder.like(mapping.getTypeValue().getColumnName(), value);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private String likeExp(String value) {
        return String.format("like '%%%s%%'", value.replaceAll("['%_]", ""));
    }

    private String eqExp(String value) {
        return String.format("= '%s'", value.replaceAll("['%_]", ""));
    }

    @Override
    public PageAndStatistics<Map<String, Object>> toFromDataSearchPage(Long dataTaskId, Long toMainDataId, PageSearch pageSearch)
            throws Exception {
        final DataTaskRuntimeModel model = getModelById(dataTaskId);
        final DataTaskBranchVo branch = model.getSubBranch() != null ? model.getSubBranch() : model.getMainBranch();
        pageSearch.initFinal();
        SqlBuilder mainBuilder = SqlBuilder.select(currentDbType(), model.getMainBranch().getToTable())
                .eq("del_flag", false)
                .eq("id", toMainDataId);
        // 添加过滤条件
        applyFromFilterMappings(model.getMainBranch(), mainBuilder);

        boolean mainExists = mainBuilder.executeSelectExists();
        if (!mainExists) {
            throw new RException("勾稽目标数据不存在或当前状态不可操作！");
        }
        PageAndStatistics<Map<String, Object>> page = PageAndStatistics.of(pageSearch.getCurrent(), pageSearch.getPageSize(), 0);
        final SqlBuilder sqlBuilder = SqlBuilder.select(currentDbType(), branch.getFromTable())
                .eq("del_flag", false);
        applyFromFilterMappings(branch, sqlBuilder);
        sqlBuilder.selectColumn("id", "id", true);
        if (StringUtils.hasText(branch.getFromTableStatusField()) && StringUtils.hasText(branch.getCompletionValue())) {
            // 过滤已完成
            // ! 不过滤，因为要支持修改
            // sqlBuilder.ne(branch.getFromTableStatusField(), branch.getCompletionValue());
            sqlBuilder.selectColumn(branch.getFromTableStatusField());
        }

        if (StringUtils.hasText(branch.getFromTableDetectionField())) {
            // 过滤检测字段
            sqlBuilder.in(branch.getFromTableDetectionField(), List.of("Y", "1"));
            sqlBuilder.selectColumn(branch.getFromTableDetectionField());
        }
        sqlBuilder.selectColumn(branch.getFromTableCrossCheckFields());
        if (StringUtils.hasText(branch.getFromTableCumulativeFields())) {
            sqlBuilder.selectColumn(branch.getFromTableCumulativeFields());
        }
        if (StringUtils.hasText(branch.getFromTableCrossBackFields())) {
            sqlBuilder.selectColumn(branch.getFromTableCrossBackFields());
        }
        // 过滤大于零的可勾数量
        // ! 不过滤，因为要支持修改
        // sqlBuilder.gt(branch.getFromTableCrossCheckFields(), 0);
        Map<String, String> dataFieldJoinMaps = appendMappingFieldsJoin(sqlBuilder, branch.getMappings());
        for (DataTaskMappingItemVo mapping : branch.getMappings()) {
            //id重复拼接问题 20230804
            String columnName = mapping.getTypeValue().getColumnName();
            if (DataTaskMappingTypes.FIELD.equals(mapping.getType()) && !"id".equalsIgnoreCase(columnName) ) {
                String selectSql = dataFieldJoinMaps.get(columnName);
                if (StringUtils.hasText(selectSql)) {
                    sqlBuilder.selectExpression(selectSql, columnName);
                } else {
                    sqlBuilder.selectColumn(columnName);
                }
            }
        }

        // 过滤数量为零或历史中没有
        if(!Boolean.TRUE.equals(model.getMainBranch().getIsHCCrossChecking())){
            sqlBuilder.orGroup(orParts -> {
                List<SqlBuilder.ConditionPart> numConditionParts = new ArrayList<>();

                numConditionParts.add(new SqlBuilder.ConditionPart()
                        .setTableAsName("t")
                        .setColumnName(branch.getFromTableCrossCheckFields())
                        .setConditionKind(SqlBuilder.ConditionKind.NotEquals)
                        .setConditionValue(0));

                // 包括回退数量字段
                if(StringUtils.hasText(branch.getFromTableCrossBackFields())) {
                    List<SqlBuilder.ConditionPart> backNumConditionParts = new ArrayList<>();
                    backNumConditionParts.add(new SqlBuilder.ConditionPart()
                            .setTableAsName("t")
                            .setColumnName(branch.getFromTableCrossCheckFields())
                            .setConditionKind(SqlBuilder.ConditionKind.IsNull));
                    backNumConditionParts.add(new SqlBuilder.ConditionPart()
                            .setTableAsName("t")
                            .setColumnName(branch.getFromTableCrossBackFields())
                            .setConditionKind(SqlBuilder.ConditionKind.NotEquals)
                            .setConditionValue(0));
                    numConditionParts.add(new SqlBuilder.ConditionPart()
                            .setConditionKind(SqlBuilder.ConditionKind.AndGroup)
                            .setConditionValue(backNumConditionParts)
                    );
                }

                orParts.add(new SqlBuilder.ConditionPart()
                        .setConditionKind(SqlBuilder.ConditionKind.OrGroup)
                        .setConditionValue(numConditionParts));

//            orParts.add(new SqlBuilder.ConditionPart()
//                    .setTableAsName("t")
//                    .setColumnName(branch.getFromTableCrossCheckFields())
//                    .setConditionKind(SqlBuilder.ConditionKind.NotEquals)
//                    .setConditionValue(0));
                orParts.add(new SqlBuilder.ConditionPart()
                        .setTableAsName("logs")
                        .setColumnName("to_sub_id")
                        .setConditionKind(SqlBuilder.ConditionKind.IsNotNull));
            });
        }

        appendMappingSelectJoin(sqlBuilder, model.getSubBranch(), "t", false, null);

        if (model.getSubBranch() != null) {
            final SqlBuilder.JoinBuilder[] fromMainJoinBuilder = { null };
            // 添加源主表 JOIN 和过滤
            sqlBuilder.innerJoin(
                    model.getMainBranch().getFromTable(),
                    "m",
                    joinBuilder -> {
                        joinBuilder.onPart(model.getSubBranch().getFromAssociation(),
                                model.getMainBranch().getFromAssociation());
                        fromMainJoinBuilder[0] = joinBuilder;
                    },
                    joinSqlBuilder -> {
                        joinSqlBuilder.eq("del_flag", 0);
                        joinSqlBuilder.selectColumn("id", "main.id", true);
                        if (StringUtils.hasText(model.getMainBranch().getFromTableStatusField())
                                && StringUtils.hasText(model.getMainBranch().getCompletionValue())) {
                            // 过滤已完成
                            joinSqlBuilder.selectColumn(model.getMainBranch().getFromTableStatusField(),
                                    "main." + model.getMainBranch().getFromTableStatusField());
                            joinSqlBuilder.ne(model.getMainBranch().getFromTableStatusField(),
                                    model.getMainBranch().getCompletionValue());
                        }
                        if (StringUtils.hasText(model.getMainBranch().getFromTableDetectionField())) {
                            // 过滤检测字段
                            joinSqlBuilder.selectColumn(model.getMainBranch().getFromTableDetectionField(),
                                    "main." + model.getMainBranch().getFromTableDetectionField());
                            joinSqlBuilder.in(model.getMainBranch().getFromTableDetectionField(), List.of("Y", "1"));
                        }
                        Map<String, String> mainJoinMaps = appendMappingFieldsJoin(joinSqlBuilder, model.getMainBranch().getMappings());
                        for (DataTaskMappingItemVo mapping : model.getMainBranch().getMappings()) {
                            if (DataTaskMappingTypes.FIELD.equals(mapping.getType())) {
                                String selectSql = mainJoinMaps.get(mapping.getTypeValue().getColumnName());
                                if (StringUtils.hasText(selectSql)) {
                                    joinSqlBuilder.selectExpression(selectSql,
                                            "main." + mapping.getTypeValue().getColumnName());
                                } else {
                                    joinSqlBuilder.selectColumn(mapping.getTypeValue().getColumnName(),
                                            "main." + mapping.getTypeValue().getColumnName());
                                }
                            }
                        }
                        fromDataSearchApply(joinSqlBuilder, model.getMainBranch(), pageSearch, false);
                        fromDataSearchApply(joinSqlBuilder, model.getSubBranch(), pageSearch, true);
                    });

            appendMappingSelectJoin(sqlBuilder, model.getMainBranch(), "m", false, "main.");

            // 关联到日志表返回
            sqlBuilder.leftJoin(
                    "data_task_logs",
                    "logs",
                    joinBuilder -> {
                        // 关联当前任务
                        joinBuilder.onPart(
                                new SqlBuilder.JoinOnConstValue(dataTaskId.toString()),
                                "data_task_id");
                        // 关联当前主表记录
                        joinBuilder.onPart("m", "id", "logs", "from_main_id");
                        // 关联当前子表记录
                        joinBuilder.onPart("t", "id", "logs", "from_sub_id");
                        // 过滤已撤消
                        joinBuilder.onPart(new SqlBuilder.JoinOnConstValue("0"), "cancelled");
                        // 当前当前目标主表记录
                        joinBuilder.onPart(
                                new SqlBuilder.JoinOnConstValue(toMainDataId.toString()),
                                "to_main_id");
                    },
                    joinSqlBuilder -> {
                        // 返回目标 id
                        joinSqlBuilder.selectColumn("to_sub_id", "logs.to_sub_id");
                        joinSqlBuilder.selectColumn("num_change", "logs.num_change");
                    });

            // 关联到目标主表做固定过滤字段处理
            sqlBuilder.innerJoin(
                    model.getMainBranch().getToTable(),
                    "tm",
                    joinBuilder -> {
                        joinBuilder.onPart(new SqlBuilder.JoinOnConstValue(toMainDataId.toString()), "id");
                        for (DataTaskMappingItemVo mapping : model.getMainBranch().getMappings()) {
                            if (DataTaskMappingTypes.FILTER.equals(mapping.getType())) {
                                String toField = mapping.getToField().trim();
                                if (toField.contains(" ")) {
                                    if (toField.matches(FILTER_TM_REGEX)) {
                                        // 引用了目标主表字段的过滤，需要加在 joinBuilder 上
                                        joinBuilder.onPart("m", mapping.getTypeValue().getColumnName(), convertFilterExpArgs(toField, toMainDataId));
                                    } else {
                                        // 包括空格的字段为只对来源主表的过滤表达式
                                        fromMainJoinBuilder[0].onPart("m", mapping.getTypeValue().getColumnName(), convertFilterExpArgs(toField, toMainDataId));
                                    }
                                } else if (mapping.getTypeValue().getColumnName().contains(" ")) {
                                    // 包括空格的源字段为只对目标主表的过滤表达式
                                    joinBuilder.onPart("tm", toField, convertFilterExpArgs(mapping.getTypeValue().getColumnName(), toMainDataId));
                                } else {
                                    // 此表对主表的过滤
                                    joinBuilder.onPart("m", mapping.getTypeValue().getColumnName(), "tm", toField);
                                }
                            }
                        }
                        for (DataTaskMappingItemVo mapping : branch.getMappings()) {
                            if (DataTaskMappingTypes.FILTER.equals(mapping.getType())) {
                                String toField = mapping.getToField().trim();
                                if (toField.contains(" ")) {
                                    if (toField.matches(FILTER_TM_REGEX)) {
                                        // 引用了目标主表字段的过滤，需要加在 joinBuilder 上
                                        joinBuilder.onPart("t", mapping.getTypeValue().getColumnName(), convertFilterExpArgs(toField, toMainDataId));
                                    } else {
                                        // 包括空格的字段为只对来源主表的过滤表达式
                                        fromMainJoinBuilder[0].onPart("t", mapping.getTypeValue().getColumnName(), convertFilterExpArgs(toField, toMainDataId));
                                    }
                                }
                            }
                        }
                    },
                    joinSqlBuilder -> {
                        joinSqlBuilder.selectColumn("id", "tm.id");
                    });

            // // 添加已有数据 JOIN
            // Map<String, String> uniqueMappings =
            // readUniqueMappings(model.getSubBranch().getUniqueMappings());
            // if(!uniqueMappings.isEmpty()) {
            // sqlBuilder.leftJoin(
            // model.getMainBranch().getToTable(),
            // "tm",
            // new SqlBuilder.JoinOnConstValue(toMainDataId.toString()),
            // "id",
            // joinSqlBuilder -> {
            // joinSqlBuilder.selectColumn("id", "toMain.id");
            // joinSqlBuilder.eq("del_flag", 0);
            // }
            // );
            // sqlBuilder.leftJoin(
            // model.getSubBranch().getToTable(),
            // "tc",
            // model.getMainBranch().getToAssociation(),
            // model.getSubBranch().getToAssociation(),
            // joinSqlBuilder -> {
            // joinSqlBuilder.selectColumn("id", "to.id");
            // }
            // );
            // }
        }

        final Long count = sqlBuilder.executeSelectCount();
        page.setTotal(count);
        if (count == 0 || count <= (long) (pageSearch.getCurrent() - 1) * pageSearch.getPageSize()) {
            return page;
        }
        sqlBuilder.skip((pageSearch.getCurrent() - 1) * pageSearch.getPageSize())
                .limit(pageSearch.getPageSize());
        page.setRecords(sqlBuilder.executeSelectList());

        // Back 字段的后置处理
        if (StringUtils.hasText(branch.getFromTableCrossBackFields())) {
            for (Map<String, Object> record : page.getRecords()) {
                if (record.get(branch.getFromTableCrossCheckFields()) == null) {
                    record.put(branch.getFromTableCrossCheckFields(), record.get(branch.getFromTableCrossBackFields()));
                }
            }
        }

        // 统计查询处理
        List<SqlBuilder.StatisticsColumn> statisticsColumns = new ArrayList<>();
        for (DataTaskMappingItemVo mapping : branch.getMappings()) {
            if (mapping.getDisplayOpt() != null && Objects.equals(mapping.getDisplayOpt().getType(), "number")) {
                if (DataTaskMappingTypes.FIELD.equals(mapping.getType()) && "sum".equals(mapping.getDisplayOpt().getStatistics())) {
                    statisticsColumns.add(SqlBuilder.StatisticsColumn.sumByColumn(mapping.getTypeValue().getColumnName(), mapping.getTypeValue().getColumnName()));
                }
            }
        }
        if (StringUtils.hasText(branch.getFromTableCrossBackFields())) {
            String tableAndCrossCheckFields = String.format("t.%s", branch.getFromTableCrossCheckFields());
            String tableAndCrossBackFields = String.format("t.%s", branch.getFromTableCrossBackFields());
            String baseSelect = sqlBuilder.getSqlHandler().funcIfNull(tableAndCrossCheckFields, tableAndCrossBackFields);
            statisticsColumns.add(SqlBuilder.StatisticsColumn.sumByExpression(baseSelect, branch.getFromTableCrossCheckFields()));
        } else {
            statisticsColumns.add(SqlBuilder.StatisticsColumn.sumByColumn(branch.getFromTableCrossCheckFields(), branch.getFromTableCrossCheckFields()));
        }
        statisticsColumns.add(SqlBuilder.StatisticsColumn.sumByExpression("logs.num_change", "logs.num_change"));
        page.setStatistics(sqlBuilder.executeSelectStatistics(statisticsColumns));

        return page;
    }

    private static void applyFromFilterMappings(DataTaskBranchVo branch, SqlBuilder sqlBuilder, String fromTableAlias) {
        final List<DataTaskMappingItemVo> toFilterMappings = branch.getMappings().stream().filter(
                        m -> DataTaskMappingTypes.FILTER.equals(m.getType()) && m.getTypeValue().getColumnName().contains(" "))
                .collect(Collectors.toList());
        for (DataTaskMappingItemVo toFilterMapping : toFilterMappings) {
            if (StringUtils.hasText(toFilterMapping.getToField())) {
                sqlBuilder.andExp(toFilterMapping.getToField(), toFilterMapping.getTypeValue().getColumnName());
            } else {
                String exp = toFilterMapping.getTypeValue().getColumnName();
                if (StringUtils.hasText(fromTableAlias) && !"t".equals(fromTableAlias)) {
                    exp = exp.replaceAll(FILTER_T_REGEX, String.format("%s.", fromTableAlias));
                }
                sqlBuilder.andExp(null, exp);
            }
        }
    }

    private static void applyFromFilterMappings(DataTaskBranchVo branch, SqlBuilder sqlBuilder) {
        applyFromFilterMappings(branch, sqlBuilder, null);
    }

    /**
     * 添加字段上配置的 JOIN （不是只用于显示的，而是直接替换数据）
     * @param sqlBuilder SQL生成器
     * @param mappings 字段配置集合
     * @return 返回已被配置的源字段名和 select sql 对应关系
     */
    private Map<String, String> appendMappingFieldsJoin(SqlBuilder sqlBuilder, List<DataTaskMappingItemVo> mappings) {
        List<String> hadAddJoins = new ArrayList<>();
        Map<String, String> fieldAsMaps = new HashMap<>();
        for (DataTaskMappingItemVo mapping : mappings) {
            if (!DataTaskMappingTypes.FIELD.equals(mapping.getType()) || mapping.getTypeValue() == null || !StringUtils.hasText(mapping.getTypeValue().getJoinSql())) {
                continue;
            }
            if (!hadAddJoins.contains(mapping.getTypeValue().getJoinSql())) {
                sqlBuilder.getJoinBuilders().add(new SqlBuilder.JoinBuilder().setJoinKind(SqlBuilder.JoinKind.Sql).setSql(mapping.getTypeValue().getJoinSql()));
                hadAddJoins.add(mapping.getTypeValue().getJoinSql());
            }
            fieldAsMaps.put(mapping.getTypeValue().getColumnName(), mapping.getTypeValue().getSelectSql());
        }
        return fieldAsMaps;
    }

    private String convertFilterExpArgs(String exp, Long toMainDataId) {
        return com.fowo.api.common.util.StringUtils.templateReplace(exp, name -> {
            if ("toMainId".equals(name)) {
                return toMainDataId.toString();
            }
            return "";
        });
    }

    @Override
    public List<Map<String, Object>> toDataList(Long dataTaskId, Long toMainDataId) throws Exception {
        final DataTaskRuntimeModel model = getModelById(dataTaskId);
        if (model.getSubBranch() == null) {
            throw new RException("暂时不支持单源");
        }
        final SqlBuilder selectBuilder = SqlBuilder.select(currentDbType(), model.getSubBranch().getToTable())
                .eq("del_flag", false);

        // 返回当前目标编号

        // 返回本单数量
        if (Boolean.TRUE.equals(model.getSubBranch().getIsHCCrossChecking())) {
            selectBuilder.selectExpression(String.format("(t.%s * -1)", model.getSubBranch().getFirstToTableCrossCheckFields()), model.getSubBranch().getFirstToTableCrossCheckFields());
        } else {
            selectBuilder.selectColumn(model.getSubBranch().getFirstToTableCrossCheckFields());
        }

        appendMappingSelectJoin(selectBuilder, model.getSubBranch(), "t", false, null);

        // 关联历史表
        selectBuilder.innerJoin(
                "data_task_logs",
                "logs",
                joinBuilder -> {
                    // 关联任务
                    joinBuilder.onPart(new SqlBuilder.JoinOnConstValue(dataTaskId.toString()), "data_task_id");
                    // 关联目标主表
                    joinBuilder.onPart(new SqlBuilder.JoinOnConstValue(toMainDataId.toString()), "to_main_id");
                    // 关联目标编号
                    joinBuilder.onPart("id", "to_sub_id");
                    // 已撤消过滤
                    joinBuilder.onPart(new SqlBuilder.JoinOnConstValue("0"), "cancelled");
                },
                joinSqlBuilder -> {
                    // 获取历史关联 id
                    joinSqlBuilder.selectColumn("id", "logs.id", true);
                    joinSqlBuilder.selectColumn("num_change", "logs.num_change");
                    // 获取源 id 字段
                    joinSqlBuilder.selectColumn("from_main_id", "main.id", true);
                    joinSqlBuilder.selectColumn("from_sub_id", "id", true);
                });

        // 关联目标主表
        selectBuilder.innerJoin(
                model.getMainBranch().getToTable(),
                "m",
                joinBuilder -> {
                    // 主子表关联
                    joinBuilder.onPart(model.getSubBranch().getToAssociation(),
                            model.getMainBranch().getToAssociation());
                    // 删除过滤
                    joinBuilder.onPart(new SqlBuilder.JoinOnConstValue("0"), "del_flag");
                },
                joinSqlBuilder -> {
                    joinSqlBuilder.selectColumn("id", "tomain.id", true);
                    toDataAddMappingSelectColumns(joinSqlBuilder, model.getMainBranch(), "m", "main.%s", selectBuilder);
                });

        appendMappingSelectJoin(selectBuilder, model.getMainBranch(), "m", false, "main.");

        // 关联源子表
        selectBuilder.innerJoin(
                model.getSubBranch().getFromTable(),
                "fc",
                joinBuilder -> {
                    // 关联历史的来源 id
                    joinBuilder.onPart("logs", "from_sub_id", "fc", "id");
                },
                joinSqlBuilder -> {
                    // 可用数量 _fromTableCrossCheckFields
                    if (Boolean.TRUE.equals(model.getSubBranch().getIsHCCrossChecking())) {
                        joinSqlBuilder
                                .selectExpression(
                                        String.format("(-t.%s + fc.%s)", model.getSubBranch().getFirstToTableCrossCheckFields(),
                                                model.getSubBranch().getFromTableCrossCheckFields()),
                                        "_fromTableCrossCheckFields");
                    } else {
                        joinSqlBuilder
                                .selectExpression(
                                        String.format("(t.%s + fc.%s)", model.getSubBranch().getFirstToTableCrossCheckFields(),
                                                model.getSubBranch().getFromTableCrossCheckFields()),
                                        "_fromTableCrossCheckFields");
                    }
                });

        toDataAddMappingSelectColumns(selectBuilder, model.getSubBranch(), "t", "%s", selectBuilder);

        return selectBuilder.executeSelectList();
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void pullUpDataTaskLog(Long dataTaskId, Long toMainId) throws Exception {
        final Long opId = (Long) identifierGenerator.nextId(null);
        // 查任务表
        final DataTaskRuntimeModel model = getModelById(dataTaskId);
        if (model == null) {
            throw new Exception("指定的配置不存在！");
        }
        if (StrUtil.isEmpty(model.getSubBranch().getToTableBdIdColumn())) {
            throw new Exception("该数据事务未配置目标表子表ID列");
        }
        //错误提示列
        String toField = null;
        String fieldDescription = null;
        List<DataTaskMappingItemVo> dataTaskMappingItemVoList = model.getSubBranch().getMappings().stream().filter(item -> item.getErrorEcho() == true).collect(Collectors.toList());
        if (!dataTaskMappingItemVoList.isEmpty()) {
            DataTaskMappingItemVo dataTaskMappingItemVo = dataTaskMappingItemVoList.get(0);
            toField = dataTaskMappingItemVo.getToField();
            fieldDescription = dataTaskMappingItemVo.getFieldDescription();
        }
        /**
         * 根据目标主键Id查询子表导入的商品信息
         */
        List<Map<String, Object>> toTableMap = SqlBuilder.select(currentDbType(), model.getSubBranch().getToTable())
                .eq("form_id", toMainId)
                .eq("del_flag", 0)
                .executeSelectList();
        if (toTableMap.isEmpty() || toTableMap.size() < 0) {
            throw new Exception("所查的目标数据不存在！");
        }
        //查原表的商品数据
        List<Map<String, Object>> records = SqlBuilder.select(currentDbType(), model.getSubBranch().getFromTable())
                .in("id", toTableMap.stream().map(e -> e.get(model.getSubBranch().getToTableBdIdColumn()).toString()).collect(Collectors.toList())).eq("del_flag", 0).executeSelectList();
        if (records.isEmpty() || records.size() < 0) {
            throw new RuntimeException("所选原表数据发生变更或不存在！");
        }

        for (Map<String, Object> map : toTableMap) {
            Map<String, Object> record = records.stream().filter(r -> Long.compare(Long.parseLong(r.get("id").toString()), Long.parseLong(map.get(model.getSubBranch().getToTableBdIdColumn()).toString())) == 0).findFirst().orElse(null);
            if (record == null) {
                throw new Exception("所选源数据在操作期间发生变更！源子表记录未找到！" + fieldDescription + "为:" + map.get(toField));
            }
            //源表勾稽字段from_table_cross_check_fieids 代表这一行还剩多少可以勾稽的数量     源表钩稽累积字段from_table_cumulative_fieids:代表这一行已经勾稽了多少数量
            // 取出原表可操作数量
            Number fromTableNumber = (Number) record.get(model.getSubBranch().getFromTableCrossCheckFields());
            Boolean flag = false;
            if (fromTableNumber == null) {
                flag = true;
                fromTableNumber = (Number) record.get(model.getSubBranch().getFromTableCrossBackFields());
            }
            // 取出目标表存入的商品的数量
            Number toTableNumber = (Number) map.get(model.getSubBranch().getFirstToTableCrossCheckFields());
            Map<String, Object> backDetail = new HashMap<>();
            Number subtract = NumberUtil.subtract(fromTableNumber, toTableNumber);
            backDetail.put(model.getSubBranch().getFromTableCrossCheckFields(), subtract);

            if (fromTableNumber != null && NumberUtil.compareTo(new BigDecimal(fromTableNumber.toString()).abs(), new BigDecimal(toTableNumber.toString()).abs()) == -1) {
                if (StringUtils.hasText(fieldDescription) && StringUtils.hasText(toField)) {
                    throw new Exception("该商品剩余勾稽数量不足,关联失败! 商品" + fieldDescription + "为:" + record.get(toField));
                } else {
                    throw new Exception("该商品剩余勾稽数量不足,关联失败! 目标表表体id为:" + map.get("id").toString() + "品名为：" + map.get("product_name") + ",原表表体id为:" + record.get("id").toString());
                }
            }

            if (StringUtils.hasText(model.getSubBranch().getFromTableCumulativeFields())) {
                Number fromTableCumlativeNumber = (Number) record
                        .get(model.getSubBranch().getFromTableCumulativeFields());
                if (fromTableCumlativeNumber == null) {
                    fromTableCumlativeNumber = 0;
                }
                Number add = NumberUtil.add(fromTableCumlativeNumber, toTableNumber);
                backDetail.put(model.getSubBranch().getFromTableCumulativeFields(), add);
            }
            // 存入原表 修改原表数据
            SqlBuilder update = SqlBuilder.update(currentDbType(), model.getSubBranch().getFromTable());
            // 更新记录原数量，作为同步版本安全识别
            update.setMap(backDetail);
            if (Boolean.TRUE.equals(flag)) {
                update.eq(model.getSubBranch().getFromTableCrossBackFields(), record.get(model.getSubBranch().getFromTableCrossBackFields()));
            } else {
                update.eq(model.getSubBranch().getFromTableCrossCheckFields(), record.get(model.getSubBranch().getFromTableCrossCheckFields()));
            }
            update.eq("id", record.get("id"));
            if (update.executeUpdate() != 1) {
                throw new Exception("修改原表数据失败!");
            }
            // 生成log表日志
            PushDownRequestPo requestPo = new PushDownRequestPo();
            requestPo.setDataTaskId(dataTaskId);
            requestPo.setToMainId(toMainId);
            //存入日志表
            writePullLog(requestPo, opId, currentUserId(), map.get(model.getSubBranch().getToTableIdColumn()).toString(), record, toTableNumber, map, Long.valueOf(map.get("id").toString()));
        }
    }

    private void toDataAddMappingSelectColumns(SqlBuilder selectBuilder, DataTaskBranchVo branch, String toAsName,
                                               String asNameFormat, SqlBuilder rootSelectBuilder) {
        if (branch.getMappings() == null || branch.getMappings().isEmpty()) {
            return;
        }
        List<DataTaskMappingItemVo> displayOnlyMappings = new ArrayList<>();
        for (DataTaskMappingItemVo mapping : branch.getMappings()) {
            if (DataTaskMappingTypes.FIELD.equals(mapping.getType())) {
                if ("_".equals(mapping.getToField())) {
                    displayOnlyMappings.add(mapping);
                } else {
                    selectBuilder.selectColumn(mapping.getToField(), String.format(asNameFormat, mapping.getToField()));
                }
            }
        }
        if (displayOnlyMappings.isEmpty()) {
            return;
        }
        boolean isMain = "main".equals(branch.getType());
        String asName = isMain ? "tm" : "tc";
        // 关联源表
        rootSelectBuilder.innerJoin(
                branch.getFromTable(),
                asName,
                joinBuilder -> {
                    joinBuilder.onPart(asName, "id", "logs", isMain ? "from_main_id" : "from_sub_id");
                },
                joinSelectBuilder -> {
                    for (DataTaskMappingItemVo displayOnlyMapping : displayOnlyMappings) {
                        joinSelectBuilder.selectColumn(displayOnlyMapping.getTypeValue().getColumnName(),
                                String.format("temp.m%d", displayOnlyMapping.getId()));
                    }
                });
    }

    private Map<String, String> readUniqueMappings(String uniqueMappings) {
        Map<String, String> map = new HashMap<>();
        for (String part : uniqueMappings.split(",")) {
            final String[] split = part.split(">");
            if (split.length == 2) {
                map.put(split[0].trim(), split[1]);
            }
        }
        return map;
    }

    private Page<Map<String, Object>> toFromDataSearchPageWithNew(DataTaskRuntimeModel model, PageSearch pageSearch)
            throws Exception {
        final DataTaskBranchVo branch = model.getSubBranch() != null ? model.getSubBranch() : model.getMainBranch();
        Page<Map<String, Object>> page = Page.of(pageSearch.getCurrent(), pageSearch.getPageSize(), 0);

        final SqlBuilder sqlBuilder = SqlBuilder.select(currentDbType(), branch.getFromTable())
                .eq("del_flag", false);
        sqlBuilder.selectColumn("id");
        if (StringUtils.hasText(branch.getFromTableStatusField()) && StringUtils.hasText(branch.getCompletionValue())) {
            // 过滤已完成
            sqlBuilder.ne(branch.getFromTableStatusField(), branch.getCompletionValue());
            sqlBuilder.selectColumn(branch.getFromTableStatusField());
        }
        if (StringUtils.hasText(branch.getFromTableDetectionField())) {
            // 过滤检测字段
            sqlBuilder.in(branch.getFromTableDetectionField(), List.of("Y", "1"));
            sqlBuilder.selectColumn(branch.getFromTableDetectionField());
        }
        sqlBuilder.selectColumn(branch.getFromTableCrossCheckFields());
        if (StringUtils.hasText(branch.getFromTableCumulativeFields())) {
            sqlBuilder.selectColumn(branch.getFromTableCumulativeFields());
        }
        // 过滤大于零的可勾数量
        sqlBuilder.gt(branch.getFromTableCrossCheckFields(), 0);
        for (DataTaskMappingItemVo mapping : branch.getMappings()) {
            if (DataTaskMappingTypes.FIELD.equals(mapping.getType())) {
                sqlBuilder.selectColumn(mapping.getTypeValue().getColumnName());
            }
        }

        if (model.getSubBranch() != null) {
            // 添加主表 JOIN 和过滤
            sqlBuilder.innerJoin(
                    model.getMainBranch().getFromTable(),
                    "m",
                    model.getSubBranch().getFromAssociation(),
                    model.getMainBranch().getFromAssociation(),
                    joinSqlBuilder -> {
                        joinSqlBuilder.selectColumn("id", "main.id");
                        if (StringUtils.hasText(model.getMainBranch().getFromTableStatusField())
                                && StringUtils.hasText(model.getMainBranch().getCompletionValue())) {
                            // 过滤已完成
                            joinSqlBuilder.selectColumn(model.getMainBranch().getFromTableStatusField(),
                                    "main." + model.getMainBranch().getFromTableStatusField());
                            joinSqlBuilder.ne(model.getMainBranch().getFromTableStatusField(),
                                    model.getMainBranch().getCompletionValue());
                        }
                        if (StringUtils.hasText(model.getMainBranch().getFromTableDetectionField())) {
                            // 过滤检测字段
                            joinSqlBuilder.selectColumn(model.getMainBranch().getFromTableDetectionField(),
                                    "main." + model.getMainBranch().getFromTableDetectionField());
                            joinSqlBuilder.in(model.getMainBranch().getFromTableDetectionField(), List.of("Y", "1"));
                        }
                        for (DataTaskMappingItemVo mapping : model.getMainBranch().getMappings()) {
                            if (DataTaskMappingTypes.FIELD.equals(mapping.getType())) {
                                joinSqlBuilder.selectColumn(mapping.getTypeValue().getColumnName(),
                                        "main." + mapping.getTypeValue().getColumnName());
                            }
                        }
                    });
        }

        final Long count = sqlBuilder.executeSelectCount();
        page.setTotal(count);
        if (count == 0 || count <= (long) (pageSearch.getCurrent() - 1) * pageSearch.getPageSize()) {
            return page;
        }
        sqlBuilder.skip((pageSearch.getCurrent() - 1) * pageSearch.getPageSize())
                .limit(pageSearch.getPageSize());
        page.setRecords(sqlBuilder.executeSelectList());
        return page;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void pushDownWholeOrder(Long dataTaskId, Long fromOrderId) throws Exception {
        final DataTaskRuntimeModel model = getModelById(dataTaskId);
        if (model == null) {
            throw new Exception("指定的配置不存在！");
        }
        HandleContext context = new HandleContext();
        context.setUserId(currentUserId());
        context.setOpId((Long) identifierGenerator.nextId(null));
        Map<String, Object> overrideVars = new HashMap<>();
        overrideVars.put(VAR_CURRENT_USER_ID, context.getUserId());
        if (model.getSubBranch() != null) {
            // 标准主子表
            final Map<String, Object> mainTable = getMainFromTable(model, model.getMainBranch(), fromOrderId);
            if (isCanPushDown(model.getMainBranch(), mainTable)) {
                final List<Map<String, Object>> details = getDetailsFromTable(model, model.getSubBranch(),
                        builder -> {
                            // 填充父子关系条件
                            String parentColumn = StringUtils.hasText(model.getMainBranch().getFromAssociation())
                                    ? model.getMainBranch().getFromAssociation()
                                    : "id";
                            if (!StringUtils.hasText(model.getSubBranch().getFromAssociation())) {
                                throw new RuntimeException("子分支的源主子表关联设置不能为空");
                            }
                            builder.eq(model.getSubBranch().getFromAssociation(), mainTable.get(parentColumn));
                            if (!Boolean.TRUE.equals(model.getSubBranch().getSupportBeyond())) {
                                if (StringUtils.hasText(model.getSubBranch().getFromTableCrossBackFields())) {
                                    builder.orGroup(orGroup -> {
                                        List<SqlBuilder.ConditionPart> backNumConditionsParts = new ArrayList<>();
                                        backNumConditionsParts.add(new SqlBuilder.ConditionPart()
                                                .setColumnName(model.getSubBranch().getFromTableCrossCheckFields())
                                                .setConditionKind(SqlBuilder.ConditionKind.IsNull));

                                        if (Boolean.TRUE.equals(model.getSubBranch().getSupportNegative())) {
                                            orGroup.add(new SqlBuilder.ConditionPart()
                                                    .setColumnName(model.getSubBranch().getFromTableCrossCheckFields())
                                                    .setConditionKind(SqlBuilder.ConditionKind.NotEquals)
                                                    .setConditionValue(0)
                                            );
                                            backNumConditionsParts.add(new SqlBuilder.ConditionPart()
                                                    .setColumnName(model.getSubBranch().getFromTableCrossBackFields())
                                                    .setConditionKind(SqlBuilder.ConditionKind.NotEquals)
                                                    .setConditionValue(0)
                                            );
                                        } else {
                                            orGroup.add(new SqlBuilder.ConditionPart()
                                                    .setTableAsName("t")
                                                    .setColumnName(model.getSubBranch().getFromTableCrossCheckFields())
                                                    .setConditionKind(SqlBuilder.ConditionKind.GreaterThan)
                                                    .setConditionValue(0)
                                            );
                                            backNumConditionsParts.add(new SqlBuilder.ConditionPart()
                                                    .setTableAsName("t")
                                                    .setColumnName(model.getSubBranch().getFromTableCrossBackFields())
                                                    .setConditionKind(SqlBuilder.ConditionKind.GreaterThan)
                                                    .setConditionValue(0)
                                            );
                                        }
                                        orGroup.add(new SqlBuilder.ConditionPart()
                                                .setConditionKind(SqlBuilder.ConditionKind.AndGroup)
                                                .setConditionValue(backNumConditionsParts)
                                        );
                                    });
                                } else {
                                    if (Boolean.TRUE.equals(model.getSubBranch().getSupportNegative())) {
                                        // 被钩稽字段不为零
                                        builder.ne(model.getSubBranch().getFromTableCrossCheckFields(), 0);
                                    } else {
                                        // 被钩稽字段大于零
                                        builder.gt(model.getSubBranch().getFromTableCrossCheckFields(), 0);
                                    }
                                }
                            }
                        });
                if (details.isEmpty()) {
                    throw new Exception("当前表单没有可下推的数据");
                }

                if (StringUtils.hasText(model.getSubBranch().getFromTableCrossBackFields())) {
                    // 复制数量字段
                    for (Map<String, Object> detail : details) {
                        if (detail.get(model.getSubBranch().getFromTableCrossCheckFields()) == null) {
                            detail.put(model.getSubBranch().getFromTableCrossCheckFields(), detail.get(model.getSubBranch().getFromTableCrossBackFields()));
                        }
                    }
                }

                context.setMainSourceTo(new HandleSourceTo()
                        .setContext(context)
                        .setBranch(model.getMainBranch())
                        .setFromMap(mainTable)
                        .setToMap(new HashMap<>())
                        .setReturnFormMap(new HashMap<>()));

                // 扫描处理
                context.setDetailSourceToList(pushDownRecords(
                        details,
                        null,
                        model.getSubBranch(),
                        overrideVars,
                        detail -> (Number) detail.get(model.getSubBranch().getFromTableCrossCheckFields()),
                        null));

            } else {
                throw new Exception("数据未找到或不满足下推条件");
            }
        } else {
            // 单个主表下推
            final List<Map<String, Object>> details = getDetailsFromTable(model, model.getMainBranch(), sqlBuilder -> {
                sqlBuilder.eq("id", fromOrderId);
                if (!Boolean.TRUE.equals(model.getMainBranch().getSupportBeyond())) {
                    if (Boolean.TRUE.equals(model.getMainBranch().getSupportNegative())) {
                        // 被钩稽字段不为零
                        sqlBuilder.ne(model.getSubBranch().getFromTableCrossCheckFields(), 0);
                    } else {
                        // 被钩稽字段大于零
                        sqlBuilder.gt(model.getMainBranch().getFromTableCrossCheckFields(), 0);
                    }
                }
            });

            // 扫描处理
            context.setDetailSourceToList(pushDownRecords(
                    details,
                    null,
                    model.getMainBranch(),
                    overrideVars,
                    detail -> (Number) detail.get(model.getMainBranch().getFromTableCrossCheckFields()), null));
        }

        contextEndHandle(model, context, null);
        afterPushDown(model, context.getTraceMap());
    }

    private void contextEndHandle(DataTaskRuntimeModel model, HandleContext context, Map<String, Object> mainInputDetail) throws Exception {
        Map<String, Object> overrideVars = new HashMap<>();
        overrideVars.put(VAR_CURRENT_USER_ID, context.getUserId());
        boolean mainSubToOne = model.isMainSubToOne();
        if (context.getMainSourceTo() != null) {
            // 主输出映射处理
            if (StringUtils.hasText(model.getMainBranch().getToTableIdColumn())) {
                context.getMainSourceTo().getToMap().put(model.getMainBranch().getToTableIdColumn(),
                        context.getMainSourceTo().getFrom("id"));
            }
            /*handleMappings(context.getMainSourceTo().getBranch(), context.getMainSourceTo().getFromMap(),
                   context.getMainSourceTo().getReturnFormMap(), context.getMainSourceTo().getToMap(), mainInputDetail, overrideVars);*/
            if (mainSubToOne) {
                for (HandleSourceTo handleSourceTo : context.getDetailSourceToList()) {
                    handleMappings(context.getMainSourceTo().getBranch(), context.getMainSourceTo().getFromMap(),
                            context.getMainSourceTo().getReturnFormMap(), handleSourceTo.getToMap(), mainInputDetail, overrideVars);
                }
            }else{
                handleMappings(context.getMainSourceTo().getBranch(), context.getMainSourceTo().getFromMap(),
                        context.getMainSourceTo().getReturnFormMap(), context.getMainSourceTo().getToMap(), mainInputDetail, overrideVars);
            }
            if (!mainSubToOne) {
                // 写主输出
                context.getMainSourceTo().toMap.put("id", identifierGenerator.nextId(null));
                SqlBuilder sqlInsert = SqlBuilder.insert(currentDbType(), model.getMainBranch().getToTable());
                sqlInsert.setMap(context.getMainSourceTo().toMap);
                sqlInsert.executeInsert();
            }
            // 主子关联写入
            if (!context.getMainSourceTo().getReturnFormMap().isEmpty()) {
                // 主回写
                SqlBuilder sqlUpdate = SqlBuilder.update(currentDbType(), model.getMainBranch().getFromTable())
                        .eq("id", context.getMainSourceTo().getFrom("id"));
                sqlUpdate.setMap(context.getMainSourceTo().getReturnFormMap());
                if (sqlUpdate.executeUpdate() != 1) {
                    throw new Exception("更新数据失败");
                }
            }

            if (!mainSubToOne) {
                // 填充输出主子表关联字段
                for (HandleSourceTo handleSourceTo : context.getDetailSourceToList()) {
                    handleSourceTo.toMap.put(
                            model.getSubBranch().getToAssociation(),
                            context.getMainSourceTo().toMap.get(model.getMainBranch().getToAssociation()));
                }
            }
        }

        // 写详情输出
        for (HandleSourceTo handleSourceTo : context.getDetailSourceToList()) {
            // 源表 id 复制字段处理
            if (StringUtils.hasText(handleSourceTo.branch.getToTableIdColumn())) {
                handleSourceTo.toMap.put(handleSourceTo.branch.getToTableIdColumn(),
                        context.getMainSourceTo().fromMap.get("id"));
            }
            // 插入输出表
            Long newTargetId = (Long) identifierGenerator.nextId(null);
            handleSourceTo.toMap.put("id", newTargetId);
            SqlBuilder sqlInsert = SqlBuilder.insert(currentDbType(), handleSourceTo.branch.getToTable());
            if (mainSubToOne) {
                // 主子表到单表，复制主表上的字段配置
                // sqlInsert.setMapSkipNullValue(context.getMainSourceTo().toMap);
                sqlInsert.setMapSkipNullValue(handleSourceTo.toMap);
            }
            // 多目标数量字段处理
            for (String otherToTableCrossCheckField : context.getDetailBranch().getOtherToTableCrossCheckFields()) {
                sqlInsert.set(otherToTableCrossCheckField, handleSourceTo.toMap.get(context.getDetailBranch().getFirstToTableCrossCheckFields()));
            }
            if (Boolean.TRUE.equals(model.getMainBranch().getIsNegative())) {
                Map<String, Object> toMap = handleSourceTo.getToMap();
                toMap.put("qty", "-" + toMap.get("qty"));
                sqlInsert.setMap(handleSourceTo.toMap);
            } else {
                sqlInsert.setMap(handleSourceTo.toMap);
            }
            sqlInsert.executeInsert();

            // 写日志
            DataTaskLogs logs = new DataTaskLogs();
            logs.setId((Long) identifierGenerator.nextId(null));
            logs.setOpId(context.opId);
            logs.setDataTaskId(model.getId());
            logs.setType(DataTaskLogs.TYPE_PUSH);
            logs.setFromMainId(NumberUtil.toLong(context.getMainSourceTo().fromMap.get("id")));
            logs.setToMainId(NumberUtil.toLong(context.getMainSourceTo().toMap.get("id")));
            logs.setFromSubId(NumberUtil.toLong(handleSourceTo.getFrom("id")));
            logs.setToSubId(newTargetId);
            logs.setNumChange(
                    NumberUtil.toDecimal(handleSourceTo.toMap.get(handleSourceTo.branch.getFirstToTableCrossCheckFields())));
            logs.setFromRow(handleSourceTo.getFromMap());
            logs.setToRow(handleSourceTo.getToMap());
            logs.setCreateTime(new Date());
            logs.setCreateUser(context.userId);
            logs.setCancelled(false);
            writeTaskLog(logs);

            // 回写源表
            SqlBuilder sqlUpdate = SqlBuilder.update(currentDbType(), handleSourceTo.branch.getFromTable());
            // 主键更新条件
            sqlUpdate.eq("id", handleSourceTo.getFrom("id"));
            // 更新同时要求之前的值不变，作为版本检查条件
            if (StringUtils.hasText(handleSourceTo.getBranch().getFromTableCrossBackFields()) && handleSourceTo.getFrom(handleSourceTo.getBranch().getFromTableCrossBackFields()) != null) {
                sqlUpdate.orGroup(orGroup -> {
                    Object conditionValue = handleSourceTo.getFrom(handleSourceTo.getBranch().getFromTableCrossCheckFields());
                    if (conditionValue == null && StringUtils.hasText(handleSourceTo.getBranch().getFromTableCrossBackFields())) {
                        conditionValue = handleSourceTo.getFrom(handleSourceTo.getBranch().getFromTableCrossBackFields());
                    }
                    orGroup.add(new SqlBuilder.ConditionPart()
                            .setColumnName(handleSourceTo.getBranch().getFromTableCrossCheckFields())
                            .setConditionKind(SqlBuilder.ConditionKind.Equals)
                            .setConditionValue(conditionValue)
                    );
                    List<SqlBuilder.ConditionPart> backNumConditions = new ArrayList<>();
                    backNumConditions.add(new SqlBuilder.ConditionPart()
                            .setColumnName(handleSourceTo.getBranch().getFromTableCrossCheckFields())
                            .setConditionKind(SqlBuilder.ConditionKind.IsNull)
                    );
                    backNumConditions.add(new SqlBuilder.ConditionPart()
                            .setColumnName(handleSourceTo.getBranch().getFromTableCrossBackFields())
                            .setConditionKind(SqlBuilder.ConditionKind.Equals)
                            .setConditionValue(handleSourceTo.getFrom(handleSourceTo.getBranch().getFromTableCrossBackFields()))
                    );
                    orGroup.add(new SqlBuilder.ConditionPart()
                            .setConditionKind(SqlBuilder.ConditionKind.AndGroup)
                            .setConditionValue(backNumConditions)
                    );
                });
            } else {
                sqlUpdate.eq(handleSourceTo.getBranch().getFromTableCrossCheckFields(),
                        handleSourceTo.getFrom(handleSourceTo.getBranch().getFromTableCrossCheckFields()));
            }
            // 设置要更新的值
            sqlUpdate.setMap(handleSourceTo.getReturnFormMap());
            if (sqlUpdate.executeUpdate() != 1) {
                throw new Exception("数据回写不正常，源数据可能已变更");
            }
        }

        // 如果主表配置存在状态字段，最后判断更新
        if (context.getMainSourceTo() != null
                && StringUtils.hasText(context.getMainSourceTo().getBranch().getFromTableStatusField())) {
            updateMainTableStatus(context);
        }
    }

    /**
     * 当主子表结构的情况下，主表有配置状态字段时，检查更新主表的状态字段
     */
    private void updateMainTableStatus(HandleContext context) throws Exception {
        // 查所有大于 0 的记录数量
        final DataTaskBranchVo detailBranch = context.getDetailBranch();
        final SqlBuilder sqlBuilder = SqlBuilder.select(currentDbType(), detailBranch.getFromTable())
                .eq("del_flag", false)
                // 主表关系
                .in(context.getDetailBranch().getFromAssociation(),
                        SqlBuilder.select(currentDbType(), context.getMainSourceTo().branch.getFromTable())
                                .setAsName("m")
                                .eq("id", context.getMainSourceTo().getFrom("id"))
                                .selectColumn(context.mainSourceTo.branch.getFromAssociation()));
        if (StringUtils.hasText(detailBranch.getFromTableCrossBackFields())) {
            sqlBuilder.orGroup(parts -> {
                // 大于零的数量
                parts.add(new SqlBuilder.ConditionPart()
                        .setColumnName(detailBranch.getFromTableCrossCheckFields())
                        .setConditionKind(SqlBuilder.ConditionKind.GreaterThan)
                        .setConditionValue(0)
                );
                // 或主数量空的，后备数量大于零
                List<SqlBuilder.ConditionPart> numAndBackParts = new ArrayList<>();
                numAndBackParts.add(new SqlBuilder.ConditionPart()
                        .setColumnName(detailBranch.getFromTableCrossCheckFields())
                        .setConditionKind(SqlBuilder.ConditionKind.IsNull));
                numAndBackParts.add(new SqlBuilder.ConditionPart()
                        .setColumnName(detailBranch.getFromTableCrossBackFields())
                        .setConditionKind(SqlBuilder.ConditionKind.GreaterThan)
                        .setConditionValue(0)
                );
                parts.add(new SqlBuilder.ConditionPart()
                        .setConditionKind(SqlBuilder.ConditionKind.AndGroup)
                        .setConditionValue(numAndBackParts)
                );
            });
        } else {
            // 大于零的数量
            sqlBuilder.gt(detailBranch.getFromTableCrossCheckFields(), 0);
        }
        if (sqlBuilder.executeSelectExists()) {
            // 存在未完成
            if (StringUtils.hasText(context.getMainSourceTo().getBranch().getMedian())) {
                SqlBuilder.update(currentDbType(), context.getMainSourceTo().getBranch().getFromTable())
                        .eq("id", context.getMainSourceTo().getFrom("id"))
                        .set(context.getMainSourceTo().getBranch().getFromTableStatusField(),
                                context.getMainSourceTo().getBranch().getMedian())
                        .executeUpdate();
            }
        } else {
            // 已全部完成
            if (StringUtils.hasText(context.getMainSourceTo().getBranch().getCompletionValue())) {
                SqlBuilder.update(currentDbType(), context.getMainSourceTo().getBranch().getFromTable())
                        .eq("id", context.getMainSourceTo().getFrom("id"))
                        .set(context.getMainSourceTo().getBranch().getFromTableStatusField(),
                                context.getMainSourceTo().getBranch().getCompletionValue())
                        .executeUpdate();
            }
        }
    }

    /**
     * 下推记录批处理
     *
     * @param details      原详情记录列表
     * @param inputDetails      用户输入的详情列表
     * @param branch       分支配置
     * @param overrideVars 表达式重写变量
     * @param handler      处理器，输入原详情记录，返回在此记录上减去的数量，此处理器不应改变原 detail 上的任何数据
     * @param toMapHandler 可选，详情数据处理器
     * @return 要填充到下推表的内容
     */
    private List<HandleSourceTo> pushDownRecords(
            List<Map<String, Object>> details,
            List<Map<String, Object>> inputDetails,
            DataTaskBranchVo branch,
            Map<String, Object> overrideVars,
            Function<Map<String, Object>, Number> handler,
            FromToMapHandler toMapHandler) {
        List<HandleSourceTo> toList = new ArrayList<>();
        int detailIndex = 0;
        for (Map<String, Object> detail : details) {
            Map<String, Object> inputDetail = inputDetails != null ? inputDetails.get(detailIndex) : null;
            Number applyCrossCheck = handler.apply(detail);
            boolean numberValid = applyCrossCheck != null && NumberUtil.compareTo(applyCrossCheck, 0) > 0;
            if (!numberValid && Boolean.TRUE.equals(branch.getSupportBeyond())) {
                numberValid = applyCrossCheck != null && NumberUtil.compareTo(applyCrossCheck, 0) != 0;
            } else if (!numberValid && Boolean.TRUE.equals(branch.getSupportNegative())) {
                numberValid = applyCrossCheck != null && NumberUtil.compareTo(applyCrossCheck, 0) < 0;
            }
            if (numberValid) {
                // 减去的数量有效，开始处理
                Number rawCrossCheck = (Number) detail.get(branch.getFromTableCrossCheckFields());
                if (rawCrossCheck == null && StringUtils.hasText(branch.getFromTableCrossBackFields())) {
                    rawCrossCheck = (Number) detail.get(branch.getFromTableCrossBackFields());
                }
                Number newCrossCheck = NumberUtil.subtract(rawCrossCheck, applyCrossCheck);
                if (!Boolean.TRUE.equals(branch.getSupportBeyond())) {
                    if (Boolean.TRUE.equals(branch.getSupportNegative())
                            && NumberUtil.compareTo(rawCrossCheck, 0) < 0) {
                        // 启用负数，而且源数量为负数时
                        if (NumberUtil.compareTo(newCrossCheck, 0) > 0) {
                            throw new RuntimeException("数量错误，结果不能大于0");
                        }
                    } else {
                        if (NumberUtil.compareTo(newCrossCheck, 0) < 0) {
                            throw new RuntimeException("数量错误，结果不能小于0");
                        }
                    }
                }

                // 写目标信息
                Map<String, Object> toDetail = new HashMap<>();
                toDetail.put(branch.getFirstToTableCrossCheckFields(), applyCrossCheck);
                // 多目标数量处理
                for (String otherToTableCrossCheckField : branch.getOtherToTableCrossCheckFields()) {
                    toDetail.put(otherToTableCrossCheckField, applyCrossCheck);
                }
                // 回写源信息
                Map<String, Object> backDetail = new HashMap<>();
                HandleSourceTo detailHandleSourceTo = new HandleSourceTo()
                        .setBranch(branch)
                        .setFromMap(detail)
                        .setReturnFormMap(backDetail)
                        .setToMap(toDetail);

                // 回写减后的数量
                backDetail.put(branch.getFromTableCrossCheckFields(), newCrossCheck);
                if (StringUtils.hasText(branch.getFromTableCumulativeFields())) {
                    // 回写累积减去的数量
                    Number rawCumulative = (Number) detail.get(branch.getFromTableCumulativeFields());
                    Number newCumulative = NumberUtil.add(rawCumulative, applyCrossCheck);
                    backDetail.put(branch.getFromTableCumulativeFields(), newCumulative);
                }
                if (StringUtils.hasText(branch.getFromTableStatusField())) {
                    // 回写状态字段
                    backDetail.put(branch.getFromTableStatusField(),
                            NumberUtil.compareTo(newCrossCheck, 0) > 0 ? branch.getMedian()
                                    : branch.getCompletionValue());
                }

                // 复制目标主键
                if (StringUtils.hasText(branch.getToTableIdColumn())) {
                    toDetail.put(branch.getToTableIdColumn(), detail.get("id"));
                }
                // 处理 mappings
                overrideVars.put("numChange", applyCrossCheck);
                handleMappings(branch, detail, backDetail, toDetail, inputDetail, overrideVars);

                if (toMapHandler != null) {
                    toMapHandler.accept(detail, toDetail);
                }

                toList.add(detailHandleSourceTo);
            }
        }
        return toList;
    }

    /**
     * 字段 Mapping 处理
     *
     * @param branch       分支
     * @param detail       此分支源数据
     * @param backDetail   此分支源返写数据
     * @param toDetail     此分支目标数据
     * @param inputDetail  用户输入的目标数据
     * @param overrideVars 表达式变量重写信息
     */
    private Map<String, Object> handleMappings(DataTaskBranchVo branch, Map<String, Object> detail, Map<String, Object> backDetail,
                                               Map<String, Object> toDetail, Map<String, Object> inputDetail, Map<String, Object> overrideVars) {
        for (DataTaskMappingItemVo mapping : branch.getMappings()) {
            if ("_".equals(mapping.getToField())) {
                // 不处理的字段
                continue;
            }
            Object value = null;
            if (DataTaskMappingTypes.FIELD.equals(mapping.getType())) {
                if (!detail.containsKey(mapping.getTypeValue().getColumnName())) {
                    throw new RuntimeException("字段映射处理失败，未找到字段 " + mapping.getTypeValue().getColumnName());
                }
                value = getOrDefault(detail, backDetail, mapping.getTypeValue().getColumnName());
            } else if (DataTaskMappingTypes.VALUE.equals(mapping.getType())) {
                value = mapping.getTypeValue().getConstantValue();
            } else if (DataTaskMappingTypes.FORMULA.equals(mapping.getType())) {
                value = runExpression(mapping.getTypeValue().getExpression(), detail, backDetail, overrideVars);
            } else if (DataTaskMappingTypes.INPUT_BOX.equals(mapping.getType())
                    || DataTaskMappingTypes.SELECT_BOX.equals(mapping.getType())
                    || DataTaskMappingTypes.FILE_BOX.equals(mapping.getType())) {
                // 输入控件的类型，如果必填，还未给输出值，必须有默认值，不然不能直接处理
                if (Boolean.TRUE.equals(mapping.getTypeValue().getRequired())
                        && inputDetail != null
                        && !inputDetail.containsKey(mapping.getToField())
                        && !StringUtils.hasText((String)inputDetail.get(mapping.getToField()))) {
                    throw new RuntimeException("此事务包括必填的输入项“" + mapping.getFieldDescription() + "”还未填写");
                }
                if (inputDetail != null) {
                    value = inputDetail.get(mapping.getToField());
                } else if (StringUtils.hasText(mapping.getTypeValue().getDefaultValue())) {
                    value = mapping.getTypeValue().getDefaultValue();
                }
            } else // noinspection StatementWithEmptyBody
                if (DataTaskMappingTypes.FILTER.equals(mapping.getType())) {
                    // 忽略 Filter 类型
                } else {
                    throw new RuntimeException("当前不支持 Mapping 类型 " + mapping.getType());
                }
            if (value == null && StringUtils.hasText(mapping.getTypeValue().getDefaultValue())) {
                value = mapping.getTypeValue().getDefaultValue();
            }
            if (value != null) {
                toDetail.put(mapping.getToField(), value);
            }
        }
        return toDetail;
    }

    private Object runExpression(String expression, Map<String, Object> baseVars, Map<String, Object> superVars,
                                 Map<String, Object> overrideVars) {
        Map<String, Object> env = new HashMap<>(baseVars);
        if (superVars != null) {
            for (Map.Entry<String, Object> stringObjectEntry : superVars.entrySet()) {
                if (!env.containsKey(stringObjectEntry.getKey())) {
                    env.put(stringObjectEntry.getKey(), stringObjectEntry.getValue());
                }
            }
        }
        if (overrideVars != null) {
            env.putAll(overrideVars);
        }
        return AviatorEvaluator.execute(expression, env);
    }

    private static Object getOrDefault(Map<String, Object> defaultMap, Map<String, Object> map, String key) {
        return map.getOrDefault(key, defaultMap.get(key));
    }

    /**
     * 通过自定义条件和分支获取多个记录
     *
     * @param model           运行时模型
     * @param branch          分支信息
     * @param builderConsumer 自定义条件和
     * @return 获取到的记录列表
     */
    private List<Map<String, Object>> getDetailsFromTable(DataTaskRuntimeModel model, DataTaskBranchVo branch,
                                                          Consumer<SqlBuilder> builderConsumer) throws Exception {
        final SqlBuilder builder = SqlBuilder.select(currentDbType(), branch.getFromTable())
                .eq("del_flag", false);
        boolean addStarFlag = false;
        Map<String, String> selectJoinMapping = appendMappingFieldsJoin(builder, branch.getMappings());
        for (Map.Entry<String, String> selectOneMapping : selectJoinMapping.entrySet()) {
            builder.selectExpression(selectOneMapping.getValue(), selectOneMapping.getKey());
            addStarFlag = true;
        }
        if (addStarFlag) {
            builder.selectExpression("t.*", null);
        }

        if (builderConsumer != null) {
            builderConsumer.accept(builder);
        }
        return builder.executeSelectList();
    }

    /**
     * 通过主键和分支获取单个记录 (不处理过滤字段)
     *
     * @param model  运行时模型
     * @param branch 分支信息
     * @param id     主键
     * @return 获取到的记录，可能为空
     */
    private Map<String, Object> getMainFromTable(DataTaskRuntimeModel model, DataTaskBranchVo branch, Object id)
            throws Exception  {
        return getMainFromTable(model, branch, id, false);
    }

    /**
     * 通过主键和分支获取单个记录
     *
     * @param model  运行时模型
     * @param branch 分支信息
     * @param id     主键
     * @param handleFilterField 指定是否处理过滤字段
     * @return 获取到的记录，可能为空
     */
    private Map<String, Object> getMainFromTable(DataTaskRuntimeModel model, DataTaskBranchVo branch, Object id, boolean handleFilterField)
            throws Exception {
        final SqlBuilder builder = SqlBuilder.select(currentDbType(), branch.getFromTable())
                .eq("id", id);
        boolean needAddStarField = false;
        Map<String, String> columnAsMap = appendMappingFieldsJoin(builder, branch.getMappings());
        for (Map.Entry<String, String> columnAsSelect : columnAsMap.entrySet()) {
            builder.selectExpression(columnAsSelect.getValue(), columnAsSelect.getKey());
            needAddStarField = true;
        }
        appendMappingSelectJoin(builder, branch);
        if (needAddStarField) {
            builder.selectExpression("t.*", null);
        }
        if (handleFilterField) {
            branch.getMappings().stream().filter(
                    f -> DataTaskMappingTypes.FILTER.equals(f.getType()) &&
                            f.getToField() != null &&
                            f.getToField().contains(" ")
            ).forEach(f -> {
                // 固定值过滤字段
                builder.andExp(String.format("%s", f.getTypeValue().getColumnName()), f.getToField());
            });
        }
        return builder.executeSelect();
    }

    /**
     * 向查询添加 Mapping 配置的显示 Join 处理字段
     * @param builder 查询 Builder
     * @param branch 分支配置
     */
    private void appendMappingSelectJoin(SqlBuilder builder, DataTaskBranchVo branch) {
        appendMappingSelectJoin(builder, branch, "t", true, null);
    }

    // 替换 t. 写法为指定的别名
    private String replaceTTo(String sql, String asName) {
        return RegExUtils.replaceAll(sql, "\\bt\\.", asName + ".");
    }


    /**
     * 向查询添加 Mapping 配置的显示 Join 处理字段
     * @param builder 查询 Builder
     * @param branch 分支配置
     * @param currentTableAsName 当前表别名
     * @param addSelectStar 是否添加选择当前表所有字段
     * @param fieldAsPrefix 显示字段 as 前缀
     */
    private Map<String, String> appendMappingSelectJoin(SqlBuilder builder, DataTaskBranchVo branch, String currentTableAsName, boolean addSelectStar, String fieldAsPrefix) {
        return appendMappingSelectJoin(builder, branch, currentTableAsName, addSelectStar, fieldAsPrefix, false);
    }

    /**
     * 向查询添加 Mapping 配置的显示 Join 处理字段
     * @param builder 查询 Builder
     * @param branch 分支配置
     * @param currentTableAsName 当前表别名
     * @param addSelectStar 是否添加选择当前表所有字段
     * @param fieldAsPrefix 显示字段 as 前缀
     * @param noSelectColumn 不要添加 select 列
     */
    private Map<String, String> appendMappingSelectJoin(SqlBuilder builder, DataTaskBranchVo branch, String currentTableAsName, boolean addSelectStar, String fieldAsPrefix, boolean noSelectColumn) {
        Map<String, String> joinSelectMap = new HashMap<>();
        Map<String, List<DataTaskMappingItemVo>> joinGroupMappings = branch.getMappings().stream().filter(m -> DataTaskMappingTypes.FIELD.equals(m.getType()) && m.getDisplayOpt() != null && StringUtils.hasText(m.getDisplayOpt().getJoinSql()) && StringUtils.hasText(m.getDisplayOpt().getSelectSql())).collect(Collectors.groupingBy(m -> m.getDisplayOpt().getJoinSql()));
        for (Map.Entry<String, List<DataTaskMappingItemVo>> stringListEntry : joinGroupMappings.entrySet()) {
            String joinExp = replaceTTo(stringListEntry.getKey(), currentTableAsName);
            builder.getJoinBuilders().add(new SqlBuilder.JoinBuilder().setJoinKind(SqlBuilder.JoinKind.Sql).setSql(joinExp));
            for (DataTaskMappingItemVo dataTaskMappingItemVo : stringListEntry.getValue()) {
                String selectSql = dataTaskMappingItemVo.getDisplayOpt().getSelectSql();
                if (fieldAsPrefix != null && !noSelectColumn) {
                    builder.selectExpression(selectSql, fieldAsPrefix + dataTaskMappingItemVo.getDisplayOpt().getShowField());
                    joinSelectMap.put(selectSql, fieldAsPrefix + dataTaskMappingItemVo.getDisplayOpt().getShowField());
                } else if (!noSelectColumn) {
                    builder.selectExpression(selectSql, dataTaskMappingItemVo.getDisplayOpt().getShowField());
                    joinSelectMap.put(selectSql, dataTaskMappingItemVo.getDisplayOpt().getShowField());
                }
            }
        }
        if (addSelectStar && joinGroupMappings.size() > 0 && !noSelectColumn) {
            builder.selectExpression(String.format("%s.*", currentTableAsName), null);
        }
        return joinSelectMap;
    }

    /**
     * 判断 record 是否满足 branch 定义的可下推条件
     *
     * @param branch 分支信息
     * @param record 对应记录
     * @return 是否可下挫
     */
    private boolean isCanPushDown(DataTaskBranchVo branch, Map<String, Object> record) {
        return isCanPushDown(branch, record, false);
    }

    /**
     * 判断 record 是否满足 branch 定义的可下推条件
     *
     * @param branch             分支信息
     * @param record             对应记录
     * @param onlyDetectionField 是否只处理检测字段
     * @return 是否可下挫
     */
    private boolean isCanPushDown(DataTaskBranchVo branch, Map<String, Object> record, boolean onlyDetectionField) {
        if (record == null) {
            // 记录不存在
            return false;
        }
        // 状态字段检查
        if (!onlyDetectionField && StringUtils.hasText(branch.getFromTableStatusField())) {
            if (Objects.equals(record.get(branch.getFromTableStatusField()), branch.getCompletionValue())) {
                // 已完成状态
                return false;
            }
        }
        // 检测字段检查
        if (StringUtils.hasText(branch.getFromTableDetectionField())) {
            Object fromDetectionValue = record.get(branch.getFromTableDetectionField());
            boolean detectionPass = Objects.equals(fromDetectionValue, 1) ||
                    Objects.equals(fromDetectionValue, "1") ||
                    Objects.equals(fromDetectionValue, "Y") ||
                    Objects.equals(fromDetectionValue, true);
            if (!detectionPass) {
                // 未通过检测字段检查
                return false;
            }
        }
        return true;
    }

    private DataTaskRuntimeModel getRuntimeModelByVo(DataTaskVo vo) throws Exception {
        if (vo == null) {
            return null;
        }
        DataTaskRuntimeModel runtimeModel = new DataTaskRuntimeModel();
        BeanUtils.copyProperties(vo, runtimeModel);
        if (vo.getBranch() == null || vo.getBranch().isEmpty()) {
            throw new Exception("此任务配置异常，分支不能为空！");
        }
        final DataTaskBranchItemVo mainBranch = vo.getBranch().stream().filter(b -> "main".equals(b.getType()))
                .findFirst().orElseThrow();
        DataTaskBranchVo mainBranchVo = getBranchVoByItem(mainBranch);
        runtimeModel.setMainBranch(mainBranchVo);

        final List<DataTaskBranchItemVo> subBranchItems = vo.getBranch().stream().filter(b -> "sub".equals(b.getType()))
                .collect(Collectors.toList());
        if (subBranchItems.isEmpty()) {
            return runtimeModel;
        }
        if (subBranchItems.size() > 1) {
            throw new Exception("配置不正确，不支持多个子表分支！");
        }
        DataTaskBranchVo subBranchVo = getBranchVoByItem(subBranchItems.get(0));
        runtimeModel.setSubBranch(subBranchVo);
        return runtimeModel;
    }

    private DataTaskBranchVo getBranchVoByItem(DataTaskBranchItemVo branchItem) {
        DataTaskBranchVo branchVo = new DataTaskBranchVo();
        BeanUtils.copyProperties(branchItem, branchVo);

        DataTaskMappingSearchParamPo searchParamPo = new DataTaskMappingSearchParamPo();
        searchParamPo.setDisableDataPremissions(true);
        searchParamPo.setBranchId(branchVo.getId());
        searchParamPo.initSort("t.sort_position asc", "t.");
        final List<DataTaskMappingItemVo> mappingItemVos = dataTaskMappingService.search(searchParamPo);
        branchVo.setMappings(mappingItemVos);

        return branchVo;
    }

    /**
     * 保存一个新的数据任务日志
     *
     * @param logs 数据任务日志
     */
    private void writeTaskLog(DataTaskLogs logs) {
        // 2023/12/2 需要转换 map 中的 LocalDateTime 为 java.util.Date
        if (logs.getFromRow()!= null) {
            convertMapLocalDateTime(logs.getFromRow());
        }
        if (logs.getToRow()!= null) {
            convertMapLocalDateTime(logs.getToRow());
        }

        if (dataTaskLogsMapper.insert(logs) != 1) {
            throw new RException("无法保存数据任务日志，数据库操作异常");
        }
    }

    private void convertMapLocalDateTime(Map<String, Object> row) {
        for (Map.Entry<String, Object> stringObjectEntry : row.entrySet()) {
            if (stringObjectEntry.getValue() instanceof LocalDateTime) {
                row.put(stringObjectEntry.getKey(), Date.from(((LocalDateTime) stringObjectEntry.getValue()).atZone(ZoneId.systemDefault()).toInstant()));
            }
        }
    }

    /**
     * 获取和目标主记录相关的所有日志（不包括已取消的）
     *
     * @param dataTaskId 数据任务编号
     * @param toMainId   目标主表编号
     * @return 数据日志列表
     */
    private List<DataTaskLogs> getTaskLogs(Long dataTaskId, Long toMainId) {
        return dataTaskLogsMapper.selectList(
                Wrappers.<DataTaskLogs>query()
                        .eq("data_task_id", dataTaskId)
                        .eq("to_main_id", toMainId)
                        .eq("cancelled", false)
                        .orderByAsc("id"));
    }

    /**
     * 更新指定的数据任务日志为取消状态
     *
     * @param logId     数据任务日志
     * @param canceller 取消者编号
     */
    private void cancellationTaskLog(Long logId, Long canceller) {
        DataTaskLogs updateLogs = new DataTaskLogs();
        updateLogs.setCancelled(true);
        updateLogs.setCanceller(canceller);
        updateLogs.setCancellationTime(new Date());
        if (dataTaskLogsMapper.update(
                updateLogs,
                Wrappers.<DataTaskLogs>query()
                        .eq("id", logId)
                        .eq("cancelled", false)) != 1) {
            throw new RException("撤消失败，部分数据在操作时间已变更");
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 创建脚本环境
        AviatorEvaluator.addFunction(new NewSerialFunction());
    }

    /**
     * 保存处理上下文数据
     */
    @Getter
    @Setter
    @Accessors(chain = true)
    public static class HandleContext {
        private Long userId;
        private Long opId;
        private HandleSourceTo mainSourceTo;
        private List<HandleSourceTo> detailSourceToList;

        public DataTaskBranchVo getDetailBranch() {
            if (detailSourceToList == null || detailSourceToList.isEmpty()) {
                return null;
            }
            return detailSourceToList.get(0).getBranch();
        }

        public Map<String, Object> getTraceMap() {
            Map<String, Object> map = new HashMap<>();
            if (mainSourceTo != null) {
                map.put("fromMap", mainSourceTo.fromMap);
                map.put("toMap", mainSourceTo.toMap);
                if (detailSourceToList != null) {
                    List<Map<String, Object>> children = new ArrayList<>();
                    for (HandleSourceTo handleSourceTo : detailSourceToList) {
                        children.add(Map.of("fromMap", handleSourceTo.fromMap, "toMap", handleSourceTo.toMap));
                    }
                    map.put("children", children);
                }
            } else {
                if (detailSourceToList != null && detailSourceToList.size() > 0) {
                    HandleSourceTo handleSourceTo = detailSourceToList.get(0);
                    map.put("fromMap", handleSourceTo.fromMap);
                    map.put("toMap", handleSourceTo.toMap);
                }
            }
            return map;
        }
    }

    /**
     * 保存处理源到目标
     */
    @Getter
    @Setter
    @Accessors(chain = true)
    public static class HandleSourceTo {
        private HandleContext context;
        private DataTaskBranchVo branch;
        private Map<String, Object> fromMap;
        private Map<String, Object> returnFormMap;
        private Map<String, Object> toMap;

        public Object getFrom(String key) {
            return fromMap.get(key);
        }

        public Object getTo(String key) {
            return toMap.get(key);
        }

        public Object getToOrFrom(String key) {
            if (toMap.containsKey(key)) {
                return toMap.get(key);
            }
            return getFrom(key);
        }
    }

    @FunctionalInterface
    public interface FromToMapHandler {
        void accept(Map<String, Object> fromMap, Map<String, Object> toMap);
    }

    class NewSerialFunction extends AbstractFunction {

        @Override
        public AviatorObject call(Map<String, Object> env, AviatorObject arg1) {
            String objectName = (String) arg1.getValue(env);
            final NewSerialRequest newSerialRequest = new NewSerialRequest();
            newSerialRequest.setObjectName(objectName);
            final String newSerial = sysSerialService.getNewSerial(newSerialRequest);
            return new AviatorString(newSerial);
        }

        @Override
        public String getName() {
            return "newSerial";
        }
    }
}
